Compare commits

..

1 Commits

Author SHA1 Message Date
Stefan Wojcik
fbf32c6907 clean up field unit tests 2017-03-01 23:41:02 -05:00
12 changed files with 249 additions and 577 deletions

View File

@@ -5,22 +5,11 @@ Changelog
Development Development
=========== ===========
- (Fill this out as you fix issues and develop your features). - (Fill this out as you fix issues and develop your features).
- Fixed using sets in field choices #1481
Changes in 0.12.0
=================
- POTENTIAL BREAKING CHANGE: Fixed limit/skip/hint/batch_size chaining #1476 - POTENTIAL BREAKING CHANGE: Fixed limit/skip/hint/batch_size chaining #1476
- POTENTIAL BREAKING CHANGE: Changed a public `QuerySet.clone_into` method to a private `QuerySet._clone_into` #1476 - POTENTIAL BREAKING CHANGE: Changed a public `QuerySet.clone_into` method to a private `QuerySet._clone_into` #1476
- Fixed the way `Document.objects.create` works with duplicate IDs #1485
- Fixed connecting to a replica set with PyMongo 2.x #1436 - Fixed connecting to a replica set with PyMongo 2.x #1436
- Fixed using sets in field choices #1481
- Fixed deleting items from a `ListField` #1318
- Fixed an obscure error message when filtering by `field__in=non_iterable`. #1237 - Fixed an obscure error message when filtering by `field__in=non_iterable`. #1237
- Fixed behavior of a `dec` update operator #1450
- Added a `rename` update operator #1454
- Added validation for the `db_field` parameter #1448
- Fixed the error message displayed when querying an `EmbeddedDocumentField` by an invalid value #1440
- Fixed the error message displayed when validating unicode URLs #1486
- Raise an error when trying to save an abstract document #1449
Changes in 0.11.0 Changes in 0.11.0
================= =================

View File

@@ -340,19 +340,14 @@ Javascript code that is executed on the database server.
Counting results Counting results
---------------- ----------------
Just as with limiting and skipping results, there is a method on a Just as with limiting and skipping results, there is a method on
:class:`~mongoengine.queryset.QuerySet` object -- :class:`~mongoengine.queryset.QuerySet` objects --
:meth:`~mongoengine.queryset.QuerySet.count`:: :meth:`~mongoengine.queryset.QuerySet.count`, but there is also a more Pythonic
way of achieving this::
num_users = User.objects.count() num_users = len(User.objects)
You could technically use ``len(User.objects)`` to get the same result, but it Even if len() is the Pythonic way of counting results, keep in mind that if you concerned about performance, :meth:`~mongoengine.queryset.QuerySet.count` is the way to go since it only execute a server side count query, while len() retrieves the results, places them in cache, and finally counts them. If we compare the performance of the two operations, len() is much slower than :meth:`~mongoengine.queryset.QuerySet.count`.
would be significantly slower than :meth:`~mongoengine.queryset.QuerySet.count`.
When you execute a server-side count query, you let MongoDB do the heavy
lifting and you receive a single integer over the wire. Meanwhile, len()
retrieves all the results, places them in a local cache, and finally counts
them. If we compare the performance of the two operations, len() is much slower
than :meth:`~mongoengine.queryset.QuerySet.count`.
Further aggregation Further aggregation
------------------- -------------------

View File

@@ -6,9 +6,6 @@ Development
*********** ***********
(Fill this out whenever you introduce breaking changes to MongoEngine) (Fill this out whenever you introduce breaking changes to MongoEngine)
0.12.0
******
This release includes various fixes for the `BaseQuerySet` methods and how they This release includes various fixes for the `BaseQuerySet` methods and how they
are chained together. Since version 0.10.1 applying limit/skip/hint/batch_size are chained together. Since version 0.10.1 applying limit/skip/hint/batch_size
to an already-existing queryset wouldn't modify the underlying PyMongo cursor. to an already-existing queryset wouldn't modify the underlying PyMongo cursor.

View File

@@ -23,7 +23,7 @@ __all__ = (list(document.__all__) + list(fields.__all__) +
list(signals.__all__) + list(errors.__all__)) list(signals.__all__) + list(errors.__all__))
VERSION = (0, 12, 0) VERSION = (0, 11, 0)
def get_version(): def get_version():

View File

@@ -684,13 +684,8 @@ class BaseDocument(object):
# class if unavailable # class if unavailable
class_name = son.get('_cls', cls._class_name) class_name = son.get('_cls', cls._class_name)
# Convert SON to a data dict, making sure each key is a string and # Convert SON to a dict, making sure each key is a string
# corresponds to the right db field. data = {str(key): value for key, value in son.iteritems()}
data = {}
for key, value in son.iteritems():
key = str(key)
key = cls._db_field_map.get(key, key)
data[key] = value
# Return correct subclass for document type # Return correct subclass for document type
if class_name != cls._class_name: if class_name != cls._class_name:

View File

@@ -1,4 +1,3 @@
from collections import OrderedDict
from bson import DBRef, SON from bson import DBRef, SON
import six import six
@@ -202,10 +201,6 @@ class DeReference(object):
as_tuple = isinstance(items, tuple) as_tuple = isinstance(items, tuple)
iterator = enumerate(items) iterator = enumerate(items)
data = [] data = []
elif isinstance(items, OrderedDict):
is_list = False
iterator = items.iteritems()
data = OrderedDict()
else: else:
is_list = False is_list = False
iterator = items.iteritems() iterator = items.iteritems()

View File

@@ -2,11 +2,9 @@ import datetime
import decimal import decimal
import itertools import itertools
import re import re
import socket
import time import time
import uuid import uuid
import warnings import warnings
from collections import Mapping
from operator import itemgetter from operator import itemgetter
from bson import Binary, DBRef, ObjectId, SON from bson import Binary, DBRef, ObjectId, SON
@@ -155,105 +153,21 @@ class EmailField(StringField):
.. versionadded:: 0.4 .. versionadded:: 0.4
""" """
USER_REGEX = re.compile(
# `dot-atom` defined in RFC 5322 Section 3.2.3. EMAIL_REGEX = re.compile(
r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*\Z" # dot-atom
# `quoted-string` defined in RFC 5322 Section 3.2.4. r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*"
r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"\Z)', # quoted-string
re.IGNORECASE r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-011\013\014\016-\177])*"'
# domain (max length of an ICAAN TLD is 22 characters)
r')@(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}|[A-Z0-9-]{2,}(?<!-))$', re.IGNORECASE
) )
UTF8_USER_REGEX = re.compile(
six.u(
# RFC 6531 Section 3.3 extends `atext` (used by dot-atom) to
# include `UTF8-non-ascii`.
r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z\u0080-\U0010FFFF]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z\u0080-\U0010FFFF]+)*\Z"
# `quoted-string`
r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"\Z)'
), re.IGNORECASE | re.UNICODE
)
DOMAIN_REGEX = re.compile(
r'((?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+)(?:[A-Z0-9-]{2,63}(?<!-))\Z',
re.IGNORECASE
)
error_msg = u'Invalid email address: %s'
def __init__(self, domain_whitelist=None, allow_utf8_user=False,
allow_ip_domain=False, *args, **kwargs):
"""Initialize the EmailField.
Args:
domain_whitelist (list) - list of otherwise invalid domain
names which you'd like to support.
allow_utf8_user (bool) - if True, the user part of the email
address can contain UTF8 characters.
False by default.
allow_ip_domain (bool) - if True, the domain part of the email
can be a valid IPv4 or IPv6 address.
"""
self.domain_whitelist = domain_whitelist or []
self.allow_utf8_user = allow_utf8_user
self.allow_ip_domain = allow_ip_domain
super(EmailField, self).__init__(*args, **kwargs)
def validate_user_part(self, user_part):
"""Validate the user part of the email address. Return True if
valid and False otherwise.
"""
if self.allow_utf8_user:
return self.UTF8_USER_REGEX.match(user_part)
return self.USER_REGEX.match(user_part)
def validate_domain_part(self, domain_part):
"""Validate the domain part of the email address. Return True if
valid and False otherwise.
"""
# Skip domain validation if it's in the whitelist.
if domain_part in self.domain_whitelist:
return True
if self.DOMAIN_REGEX.match(domain_part):
return True
# Validate IPv4/IPv6, e.g. user@[192.168.0.1]
if (
self.allow_ip_domain and
domain_part[0] == '[' and
domain_part[-1] == ']'
):
for addr_family in (socket.AF_INET, socket.AF_INET6):
try:
socket.inet_pton(addr_family, domain_part[1:-1])
return True
except (socket.error, UnicodeEncodeError):
pass
return False
def validate(self, value): def validate(self, value):
if not EmailField.EMAIL_REGEX.match(value):
self.error('Invalid email address: %s' % value)
super(EmailField, self).validate(value) super(EmailField, self).validate(value)
if '@' not in value:
self.error(self.error_msg % value)
user_part, domain_part = value.rsplit('@', 1)
# Validate the user part.
if not self.validate_user_part(user_part):
self.error(self.error_msg % value)
# Validate the domain and, if invalid, see if it's IDN-encoded.
if not self.validate_domain_part(domain_part):
try:
domain_part = domain_part.encode('idna').decode('ascii')
except UnicodeError:
self.error(self.error_msg % value)
else:
if not self.validate_domain_part(domain_part):
self.error(self.error_msg % value)
class IntField(BaseField): class IntField(BaseField):
"""32-bit integer field.""" """32-bit integer field."""
@@ -705,14 +619,6 @@ class DynamicField(BaseField):
Used by :class:`~mongoengine.DynamicDocument` to handle dynamic data""" Used by :class:`~mongoengine.DynamicDocument` to handle dynamic data"""
def __init__(self, container_class=dict, *args, **kwargs):
self._container_cls = container_class
if not issubclass(self._container_cls, Mapping):
self.error('The class that is specified in `container_class` parameter '
'must be a subclass of `dict`.')
super(DynamicField, self).__init__(*args, **kwargs)
def to_mongo(self, value, use_db_field=True, fields=None): def to_mongo(self, value, use_db_field=True, fields=None):
"""Convert a Python type to a MongoDB compatible type. """Convert a Python type to a MongoDB compatible type.
""" """
@@ -738,7 +644,7 @@ class DynamicField(BaseField):
is_list = True is_list = True
value = {k: v for k, v in enumerate(value)} value = {k: v for k, v in enumerate(value)}
data = self._container_cls() data = {}
for k, v in value.iteritems(): for k, v in value.iteritems():
data[k] = self.to_mongo(v, use_db_field, fields) data[k] = self.to_mongo(v, use_db_field, fields)
@@ -1092,8 +998,8 @@ class ReferenceField(BaseField):
def validate(self, value): def validate(self, value):
if not isinstance(value, (self.document_type, DBRef, ObjectId)): if not isinstance(value, (self.document_type, DBRef)):
self.error('A ReferenceField only accepts DBRef, ObjectId or documents') self.error('A ReferenceField only accepts DBRef or documents')
if isinstance(value, Document) and value.id is None: if isinstance(value, Document) and value.id is None:
self.error('You can only reference documents once they have been ' self.error('You can only reference documents once they have been '

View File

@@ -158,49 +158,44 @@ class BaseQuerySet(object):
# self._cursor # self._cursor
def __getitem__(self, key): def __getitem__(self, key):
"""Return a document instance corresponding to a given index if """Support skip and limit using getitem and slicing syntax."""
the key is an integer. If the key is a slice, translate its
bounds into a skip and a limit, and return a cloned queryset
with that skip/limit applied. For example:
>>> User.objects[0]
<User: User object>
>>> User.objects[1:3]
[<User: User object>, <User: User object>]
"""
queryset = self.clone() queryset = self.clone()
# Handle a slice # Slice provided
if isinstance(key, slice): if isinstance(key, slice):
queryset._cursor_obj = queryset._cursor[key] try:
queryset._skip, queryset._limit = key.start, key.stop queryset._cursor_obj = queryset._cursor[key]
if key.start and key.stop: queryset._skip, queryset._limit = key.start, key.stop
queryset._limit = key.stop - key.start if key.start and key.stop:
queryset._limit = key.stop - key.start
except IndexError as err:
# PyMongo raises an error if key.start == key.stop, catch it,
# bin it, kill it.
start = key.start or 0
if start >= 0 and key.stop >= 0 and key.step is None:
if start == key.stop:
queryset.limit(0)
queryset._skip = key.start
queryset._limit = key.stop - start
return queryset
raise err
# Allow further QuerySet modifications to be performed # Allow further QuerySet modifications to be performed
return queryset return queryset
# Integer index provided
# Handle an index
elif isinstance(key, int): elif isinstance(key, int):
if queryset._scalar: if queryset._scalar:
return queryset._get_scalar( return queryset._get_scalar(
queryset._document._from_son( queryset._document._from_son(queryset._cursor[key],
queryset._cursor[key], _auto_dereference=self._auto_dereference,
_auto_dereference=self._auto_dereference, only_fields=self.only_fields))
only_fields=self.only_fields
)
)
if queryset._as_pymongo: if queryset._as_pymongo:
return queryset._get_as_pymongo(queryset._cursor[key]) return queryset._get_as_pymongo(queryset._cursor[key])
return queryset._document._from_son(queryset._cursor[key],
_auto_dereference=self._auto_dereference,
only_fields=self.only_fields)
return queryset._document._from_son( raise AttributeError
queryset._cursor[key],
_auto_dereference=self._auto_dereference,
only_fields=self.only_fields
)
raise AttributeError('Provide a slice or an integer index')
def __iter__(self): def __iter__(self):
raise NotImplementedError raise NotImplementedError

View File

@@ -28,6 +28,8 @@ TEST_IMAGE_PATH = os.path.join(os.path.dirname(__file__),
__all__ = ("InstanceTest",) __all__ = ("InstanceTest",)
class InstanceTest(unittest.TestCase): class InstanceTest(unittest.TestCase):
def setUp(self): def setUp(self):
@@ -70,7 +72,8 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(field._instance, instance) self.assertEqual(field._instance, instance)
def test_capped_collection(self): def test_capped_collection(self):
"""Ensure that capped collections work properly.""" """Ensure that capped collections work properly.
"""
class Log(Document): class Log(Document):
date = DateTimeField(default=datetime.now) date = DateTimeField(default=datetime.now)
meta = { meta = {
@@ -178,7 +181,8 @@ class InstanceTest(unittest.TestCase):
self.assertEqual('<Article: привет мир>', repr(doc)) self.assertEqual('<Article: привет мир>', repr(doc))
def test_repr_none(self): def test_repr_none(self):
"""Ensure None values are handled correctly.""" """Ensure None values handled correctly
"""
class Article(Document): class Article(Document):
title = StringField() title = StringField()
@@ -186,23 +190,25 @@ class InstanceTest(unittest.TestCase):
return None return None
doc = Article(title=u'привет мир') doc = Article(title=u'привет мир')
self.assertEqual('<Article: None>', repr(doc)) self.assertEqual('<Article: None>', repr(doc))
def test_queryset_resurrects_dropped_collection(self): def test_queryset_resurrects_dropped_collection(self):
self.Person.drop_collection() self.Person.drop_collection()
self.assertEqual([], list(self.Person.objects())) self.assertEqual([], list(self.Person.objects()))
# Ensure works correctly with inhertited classes
class Actor(self.Person): class Actor(self.Person):
pass pass
# Ensure works correctly with inhertited classes
Actor.objects() Actor.objects()
self.Person.drop_collection() self.Person.drop_collection()
self.assertEqual([], list(Actor.objects())) self.assertEqual([], list(Actor.objects()))
def test_polymorphic_references(self): def test_polymorphic_references(self):
"""Ensure that the correct subclasses are returned from a query """Ensure that the correct subclasses are returned from a query when
when using references / generic references using references / generic references
""" """
class Animal(Document): class Animal(Document):
meta = {'allow_inheritance': True} meta = {'allow_inheritance': True}
@@ -252,6 +258,9 @@ class InstanceTest(unittest.TestCase):
classes = [a.__class__ for a in Zoo.objects.first().animals] classes = [a.__class__ for a in Zoo.objects.first().animals]
self.assertEqual(classes, [Animal, Fish, Mammal, Dog, Human]) self.assertEqual(classes, [Animal, Fish, Mammal, Dog, Human])
Zoo.drop_collection()
Animal.drop_collection()
def test_reference_inheritance(self): def test_reference_inheritance(self):
class Stats(Document): class Stats(Document):
created = DateTimeField(default=datetime.now) created = DateTimeField(default=datetime.now)
@@ -278,7 +287,8 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(list_stats, CompareStats.objects.first().stats) self.assertEqual(list_stats, CompareStats.objects.first().stats)
def test_db_field_load(self): def test_db_field_load(self):
"""Ensure we load data correctly from the right db field.""" """Ensure we load data correctly
"""
class Person(Document): class Person(Document):
name = StringField(required=True) name = StringField(required=True)
_rank = StringField(required=False, db_field="rank") _rank = StringField(required=False, db_field="rank")
@@ -297,7 +307,8 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(Person.objects.get(name="Fred").rank, "Private") self.assertEqual(Person.objects.get(name="Fred").rank, "Private")
def test_db_embedded_doc_field_load(self): def test_db_embedded_doc_field_load(self):
"""Ensure we load embedded document data correctly.""" """Ensure we load embedded document data correctly
"""
class Rank(EmbeddedDocument): class Rank(EmbeddedDocument):
title = StringField(required=True) title = StringField(required=True)
@@ -322,7 +333,8 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(Person.objects.get(name="Fred").rank, "Private") self.assertEqual(Person.objects.get(name="Fred").rank, "Private")
def test_custom_id_field(self): def test_custom_id_field(self):
"""Ensure that documents may be created with custom primary keys.""" """Ensure that documents may be created with custom primary keys.
"""
class User(Document): class User(Document):
username = StringField(primary_key=True) username = StringField(primary_key=True)
name = StringField() name = StringField()
@@ -370,7 +382,10 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(user_son['_id'], 'mongo') self.assertEqual(user_son['_id'], 'mongo')
self.assertTrue('username' not in user_son['_id']) self.assertTrue('username' not in user_son['_id'])
User.drop_collection()
def test_document_not_registered(self): def test_document_not_registered(self):
class Place(Document): class Place(Document):
name = StringField() name = StringField()
@@ -392,6 +407,7 @@ class InstanceTest(unittest.TestCase):
list(Place.objects.all()) list(Place.objects.all())
def test_document_registry_regressions(self): def test_document_registry_regressions(self):
class Location(Document): class Location(Document):
name = StringField() name = StringField()
meta = {'allow_inheritance': True} meta = {'allow_inheritance': True}
@@ -405,16 +421,18 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(Area, get_document("Location.Area")) self.assertEqual(Area, get_document("Location.Area"))
def test_creation(self): def test_creation(self):
"""Ensure that document may be created using keyword arguments.""" """Ensure that document may be created using keyword arguments.
"""
person = self.Person(name="Test User", age=30) person = self.Person(name="Test User", age=30)
self.assertEqual(person.name, "Test User") self.assertEqual(person.name, "Test User")
self.assertEqual(person.age, 30) self.assertEqual(person.age, 30)
def test_to_dbref(self): def test_to_dbref(self):
"""Ensure that you can get a dbref of a document.""" """Ensure that you can get a dbref of a document"""
person = self.Person(name="Test User", age=30) person = self.Person(name="Test User", age=30)
self.assertRaises(OperationError, person.to_dbref) self.assertRaises(OperationError, person.to_dbref)
person.save() person.save()
person.to_dbref() person.to_dbref()
def test_save_abstract_document(self): def test_save_abstract_document(self):
@@ -427,7 +445,8 @@ class InstanceTest(unittest.TestCase):
Doc(name='aaa').save() Doc(name='aaa').save()
def test_reload(self): def test_reload(self):
"""Ensure that attributes may be reloaded.""" """Ensure that attributes may be reloaded.
"""
person = self.Person(name="Test User", age=20) person = self.Person(name="Test User", age=20)
person.save() person.save()
@@ -460,6 +479,7 @@ class InstanceTest(unittest.TestCase):
doc = Animal(superphylum='Deuterostomia') doc = Animal(superphylum='Deuterostomia')
doc.save() doc.save()
doc.reload() doc.reload()
Animal.drop_collection()
def test_reload_sharded_nested(self): def test_reload_sharded_nested(self):
class SuperPhylum(EmbeddedDocument): class SuperPhylum(EmbeddedDocument):
@@ -473,9 +493,11 @@ class InstanceTest(unittest.TestCase):
doc = Animal(superphylum=SuperPhylum(name='Deuterostomia')) doc = Animal(superphylum=SuperPhylum(name='Deuterostomia'))
doc.save() doc.save()
doc.reload() doc.reload()
Animal.drop_collection()
def test_reload_referencing(self): def test_reload_referencing(self):
"""Ensures reloading updates weakrefs correctly.""" """Ensures reloading updates weakrefs correctly
"""
class Embedded(EmbeddedDocument): class Embedded(EmbeddedDocument):
dict_field = DictField() dict_field = DictField()
list_field = ListField() list_field = ListField()
@@ -547,7 +569,8 @@ class InstanceTest(unittest.TestCase):
self.assertFalse("Threw wrong exception") self.assertFalse("Threw wrong exception")
def test_reload_of_non_strict_with_special_field_name(self): def test_reload_of_non_strict_with_special_field_name(self):
"""Ensures reloading works for documents with meta strict == False.""" """Ensures reloading works for documents with meta strict == False
"""
class Post(Document): class Post(Document):
meta = { meta = {
'strict': False 'strict': False
@@ -568,7 +591,8 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(post.items, ["more lorem", "even more ipsum"]) self.assertEqual(post.items, ["more lorem", "even more ipsum"])
def test_dictionary_access(self): def test_dictionary_access(self):
"""Ensure that dictionary-style field access works properly.""" """Ensure that dictionary-style field access works properly.
"""
person = self.Person(name='Test User', age=30, job=self.Job()) person = self.Person(name='Test User', age=30, job=self.Job())
self.assertEqual(person['name'], 'Test User') self.assertEqual(person['name'], 'Test User')
@@ -610,7 +634,8 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(sub_doc.to_mongo().keys(), ['id']) self.assertEqual(sub_doc.to_mongo().keys(), ['id'])
def test_embedded_document(self): def test_embedded_document(self):
"""Ensure that embedded documents are set up correctly.""" """Ensure that embedded documents are set up correctly.
"""
class Comment(EmbeddedDocument): class Comment(EmbeddedDocument):
content = StringField() content = StringField()
@@ -618,7 +643,8 @@ class InstanceTest(unittest.TestCase):
self.assertFalse('id' in Comment._fields) self.assertFalse('id' in Comment._fields)
def test_embedded_document_instance(self): def test_embedded_document_instance(self):
"""Ensure that embedded documents can reference parent instance.""" """Ensure that embedded documents can reference parent instance
"""
class Embedded(EmbeddedDocument): class Embedded(EmbeddedDocument):
string = StringField() string = StringField()
@@ -626,7 +652,6 @@ class InstanceTest(unittest.TestCase):
embedded_field = EmbeddedDocumentField(Embedded) embedded_field = EmbeddedDocumentField(Embedded)
Doc.drop_collection() Doc.drop_collection()
doc = Doc(embedded_field=Embedded(string="Hi")) doc = Doc(embedded_field=Embedded(string="Hi"))
self.assertHasInstance(doc.embedded_field, doc) self.assertHasInstance(doc.embedded_field, doc)
@@ -636,8 +661,7 @@ class InstanceTest(unittest.TestCase):
def test_embedded_document_complex_instance(self): def test_embedded_document_complex_instance(self):
"""Ensure that embedded documents in complex fields can reference """Ensure that embedded documents in complex fields can reference
parent instance. parent instance"""
"""
class Embedded(EmbeddedDocument): class Embedded(EmbeddedDocument):
string = StringField() string = StringField()
@@ -653,7 +677,8 @@ class InstanceTest(unittest.TestCase):
self.assertHasInstance(doc.embedded_field[0], doc) self.assertHasInstance(doc.embedded_field[0], doc)
def test_embedded_document_complex_instance_no_use_db_field(self): def test_embedded_document_complex_instance_no_use_db_field(self):
"""Ensure that use_db_field is propagated to list of Emb Docs.""" """Ensure that use_db_field is propagated to list of Emb Docs
"""
class Embedded(EmbeddedDocument): class Embedded(EmbeddedDocument):
string = StringField(db_field='s') string = StringField(db_field='s')
@@ -665,6 +690,7 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(d['embedded_field'], [{'string': 'Hi'}]) self.assertEqual(d['embedded_field'], [{'string': 'Hi'}])
def test_instance_is_set_on_setattr(self): def test_instance_is_set_on_setattr(self):
class Email(EmbeddedDocument): class Email(EmbeddedDocument):
email = EmailField() email = EmailField()
@@ -672,7 +698,6 @@ class InstanceTest(unittest.TestCase):
email = EmbeddedDocumentField(Email) email = EmbeddedDocumentField(Email)
Account.drop_collection() Account.drop_collection()
acc = Account() acc = Account()
acc.email = Email(email='test@example.com') acc.email = Email(email='test@example.com')
self.assertHasInstance(acc._data["email"], acc) self.assertHasInstance(acc._data["email"], acc)
@@ -682,6 +707,7 @@ class InstanceTest(unittest.TestCase):
self.assertHasInstance(acc1._data["email"], acc1) self.assertHasInstance(acc1._data["email"], acc1)
def test_instance_is_set_on_setattr_on_embedded_document_list(self): def test_instance_is_set_on_setattr_on_embedded_document_list(self):
class Email(EmbeddedDocument): class Email(EmbeddedDocument):
email = EmailField() email = EmailField()
@@ -827,28 +853,32 @@ class InstanceTest(unittest.TestCase):
self.assertDbEqual([dict(other_doc.to_mongo()), dict(doc.to_mongo())]) self.assertDbEqual([dict(other_doc.to_mongo()), dict(doc.to_mongo())])
def test_save(self): def test_save(self):
"""Ensure that a document may be saved in the database.""" """Ensure that a document may be saved in the database.
"""
# Create person object and save it to the database # Create person object and save it to the database
person = self.Person(name='Test User', age=30) person = self.Person(name='Test User', age=30)
person.save() person.save()
# Ensure that the object is in the database # Ensure that the object is in the database
collection = self.db[self.Person._get_collection_name()] collection = self.db[self.Person._get_collection_name()]
person_obj = collection.find_one({'name': 'Test User'}) person_obj = collection.find_one({'name': 'Test User'})
self.assertEqual(person_obj['name'], 'Test User') self.assertEqual(person_obj['name'], 'Test User')
self.assertEqual(person_obj['age'], 30) self.assertEqual(person_obj['age'], 30)
self.assertEqual(person_obj['_id'], person.id) self.assertEqual(person_obj['_id'], person.id)
# Test skipping validation on save # Test skipping validation on save
class Recipient(Document): class Recipient(Document):
email = EmailField(required=True) email = EmailField(required=True)
recipient = Recipient(email='not-an-email') recipient = Recipient(email='root@localhost')
self.assertRaises(ValidationError, recipient.save) self.assertRaises(ValidationError, recipient.save)
recipient.save(validate=False)
try:
recipient.save(validate=False)
except ValidationError:
self.fail()
def test_save_to_a_value_that_equates_to_false(self): def test_save_to_a_value_that_equates_to_false(self):
class Thing(EmbeddedDocument): class Thing(EmbeddedDocument):
count = IntField() count = IntField()
@@ -868,6 +898,7 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(user.thing.count, 0) self.assertEqual(user.thing.count, 0)
def test_save_max_recursion_not_hit(self): def test_save_max_recursion_not_hit(self):
class Person(Document): class Person(Document):
name = StringField() name = StringField()
parent = ReferenceField('self') parent = ReferenceField('self')
@@ -893,6 +924,7 @@ class InstanceTest(unittest.TestCase):
p0.save() p0.save()
def test_save_max_recursion_not_hit_with_file_field(self): def test_save_max_recursion_not_hit_with_file_field(self):
class Foo(Document): class Foo(Document):
name = StringField() name = StringField()
picture = FileField() picture = FileField()
@@ -916,6 +948,7 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(b.picture, b.bar.picture, b.bar.bar.picture) self.assertEqual(b.picture, b.bar.picture, b.bar.bar.picture)
def test_save_cascades(self): def test_save_cascades(self):
class Person(Document): class Person(Document):
name = StringField() name = StringField()
parent = ReferenceField('self') parent = ReferenceField('self')
@@ -938,6 +971,7 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(p1.name, p.parent.name) self.assertEqual(p1.name, p.parent.name)
def test_save_cascade_kwargs(self): def test_save_cascade_kwargs(self):
class Person(Document): class Person(Document):
name = StringField() name = StringField()
parent = ReferenceField('self') parent = ReferenceField('self')
@@ -958,6 +992,7 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(p1.name, p2.parent.name) self.assertEqual(p1.name, p2.parent.name)
def test_save_cascade_meta_false(self): def test_save_cascade_meta_false(self):
class Person(Document): class Person(Document):
name = StringField() name = StringField()
parent = ReferenceField('self') parent = ReferenceField('self')
@@ -986,6 +1021,7 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(p1.name, p.parent.name) self.assertEqual(p1.name, p.parent.name)
def test_save_cascade_meta_true(self): def test_save_cascade_meta_true(self):
class Person(Document): class Person(Document):
name = StringField() name = StringField()
parent = ReferenceField('self') parent = ReferenceField('self')
@@ -1010,6 +1046,7 @@ class InstanceTest(unittest.TestCase):
self.assertNotEqual(p1.name, p.parent.name) self.assertNotEqual(p1.name, p.parent.name)
def test_save_cascades_generically(self): def test_save_cascades_generically(self):
class Person(Document): class Person(Document):
name = StringField() name = StringField()
parent = GenericReferenceField() parent = GenericReferenceField()
@@ -1035,6 +1072,7 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(p1.name, p.parent.name) self.assertEqual(p1.name, p.parent.name)
def test_save_atomicity_condition(self): def test_save_atomicity_condition(self):
class Widget(Document): class Widget(Document):
toggle = BooleanField(default=False) toggle = BooleanField(default=False)
count = IntField(default=0) count = IntField(default=0)
@@ -1112,8 +1150,7 @@ class InstanceTest(unittest.TestCase):
def test_update(self): def test_update(self):
"""Ensure that an existing document is updated instead of be """Ensure that an existing document is updated instead of be
overwritten. overwritten."""
"""
# Create person object and save it to the database # Create person object and save it to the database
person = self.Person(name='Test User', age=30) person = self.Person(name='Test User', age=30)
person.save() person.save()
@@ -1217,6 +1254,7 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(2, self.Person.objects.count()) self.assertEqual(2, self.Person.objects.count())
def test_can_save_if_not_included(self): def test_can_save_if_not_included(self):
class EmbeddedDoc(EmbeddedDocument): class EmbeddedDoc(EmbeddedDocument):
pass pass
@@ -1303,7 +1341,10 @@ class InstanceTest(unittest.TestCase):
doc2.update(set__name=doc1.name) doc2.update(set__name=doc1.name)
def test_embedded_update(self): def test_embedded_update(self):
"""Test update on `EmbeddedDocumentField` fields.""" """
Test update on `EmbeddedDocumentField` fields
"""
class Page(EmbeddedDocument): class Page(EmbeddedDocument):
log_message = StringField(verbose_name="Log message", log_message = StringField(verbose_name="Log message",
required=True) required=True)
@@ -1324,9 +1365,11 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(site.page.log_message, "Error: Dummy message") self.assertEqual(site.page.log_message, "Error: Dummy message")
def test_embedded_update_db_field(self): def test_embedded_update_db_field(self):
"""Test update on `EmbeddedDocumentField` fields when db_field
is other than default.
""" """
Test update on `EmbeddedDocumentField` fields when db_field is other
than default.
"""
class Page(EmbeddedDocument): class Page(EmbeddedDocument):
log_message = StringField(verbose_name="Log message", log_message = StringField(verbose_name="Log message",
db_field="page_log_message", db_field="page_log_message",
@@ -1349,7 +1392,9 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(site.page.log_message, "Error: Dummy message") self.assertEqual(site.page.log_message, "Error: Dummy message")
def test_save_only_changed_fields(self): def test_save_only_changed_fields(self):
"""Ensure save only sets / unsets changed fields.""" """Ensure save only sets / unsets changed fields
"""
class User(self.Person): class User(self.Person):
active = BooleanField(default=True) active = BooleanField(default=True)
@@ -1469,8 +1514,8 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(q, 3) self.assertEqual(q, 3)
def test_set_unset_one_operation(self): def test_set_unset_one_operation(self):
"""Ensure that $set and $unset actions are performed in the """Ensure that $set and $unset actions are performed in the same
same operation. operation.
""" """
class FooBar(Document): class FooBar(Document):
foo = StringField(default=None) foo = StringField(default=None)
@@ -1491,7 +1536,9 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(1, q) self.assertEqual(1, q)
def test_save_only_changed_fields_recursive(self): def test_save_only_changed_fields_recursive(self):
"""Ensure save only sets / unsets changed fields.""" """Ensure save only sets / unsets changed fields
"""
class Comment(EmbeddedDocument): class Comment(EmbeddedDocument):
published = BooleanField(default=True) published = BooleanField(default=True)
@@ -1531,7 +1578,8 @@ class InstanceTest(unittest.TestCase):
self.assertFalse(person.comments_dict['first_post'].published) self.assertFalse(person.comments_dict['first_post'].published)
def test_delete(self): def test_delete(self):
"""Ensure that document may be deleted using the delete method.""" """Ensure that document may be deleted using the delete method.
"""
person = self.Person(name="Test User", age=30) person = self.Person(name="Test User", age=30)
person.save() person.save()
self.assertEqual(self.Person.objects.count(), 1) self.assertEqual(self.Person.objects.count(), 1)
@@ -1539,34 +1587,33 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(self.Person.objects.count(), 0) self.assertEqual(self.Person.objects.count(), 0)
def test_save_custom_id(self): def test_save_custom_id(self):
"""Ensure that a document may be saved with a custom _id.""" """Ensure that a document may be saved with a custom _id.
"""
# Create person object and save it to the database # Create person object and save it to the database
person = self.Person(name='Test User', age=30, person = self.Person(name='Test User', age=30,
id='497ce96f395f2f052a494fd4') id='497ce96f395f2f052a494fd4')
person.save() person.save()
# Ensure that the object is in the database with the correct _id # Ensure that the object is in the database with the correct _id
collection = self.db[self.Person._get_collection_name()] collection = self.db[self.Person._get_collection_name()]
person_obj = collection.find_one({'name': 'Test User'}) person_obj = collection.find_one({'name': 'Test User'})
self.assertEqual(str(person_obj['_id']), '497ce96f395f2f052a494fd4') self.assertEqual(str(person_obj['_id']), '497ce96f395f2f052a494fd4')
def test_save_custom_pk(self): def test_save_custom_pk(self):
"""Ensure that a document may be saved with a custom _id using """
pk alias. Ensure that a document may be saved with a custom _id using pk alias.
""" """
# Create person object and save it to the database # Create person object and save it to the database
person = self.Person(name='Test User', age=30, person = self.Person(name='Test User', age=30,
pk='497ce96f395f2f052a494fd4') pk='497ce96f395f2f052a494fd4')
person.save() person.save()
# Ensure that the object is in the database with the correct _id # Ensure that the object is in the database with the correct _id
collection = self.db[self.Person._get_collection_name()] collection = self.db[self.Person._get_collection_name()]
person_obj = collection.find_one({'name': 'Test User'}) person_obj = collection.find_one({'name': 'Test User'})
self.assertEqual(str(person_obj['_id']), '497ce96f395f2f052a494fd4') self.assertEqual(str(person_obj['_id']), '497ce96f395f2f052a494fd4')
def test_save_list(self): def test_save_list(self):
"""Ensure that a list field may be properly saved.""" """Ensure that a list field may be properly saved.
"""
class Comment(EmbeddedDocument): class Comment(EmbeddedDocument):
content = StringField() content = StringField()
@@ -1589,6 +1636,8 @@ class InstanceTest(unittest.TestCase):
for comment_obj, comment in zip(post_obj['comments'], comments): for comment_obj, comment in zip(post_obj['comments'], comments):
self.assertEqual(comment_obj['content'], comment['content']) self.assertEqual(comment_obj['content'], comment['content'])
BlogPost.drop_collection()
def test_list_search_by_embedded(self): def test_list_search_by_embedded(self):
class User(Document): class User(Document):
username = StringField(required=True) username = StringField(required=True)
@@ -1648,8 +1697,8 @@ class InstanceTest(unittest.TestCase):
list(Page.objects.filter(comments__user=u3))) list(Page.objects.filter(comments__user=u3)))
def test_save_embedded_document(self): def test_save_embedded_document(self):
"""Ensure that a document with an embedded document field may """Ensure that a document with an embedded document field may be
be saved in the database. saved in the database.
""" """
class EmployeeDetails(EmbeddedDocument): class EmployeeDetails(EmbeddedDocument):
position = StringField() position = StringField()
@@ -1668,13 +1717,13 @@ class InstanceTest(unittest.TestCase):
employee_obj = collection.find_one({'name': 'Test Employee'}) employee_obj = collection.find_one({'name': 'Test Employee'})
self.assertEqual(employee_obj['name'], 'Test Employee') self.assertEqual(employee_obj['name'], 'Test Employee')
self.assertEqual(employee_obj['age'], 50) self.assertEqual(employee_obj['age'], 50)
# Ensure that the 'details' embedded object saved correctly # Ensure that the 'details' embedded object saved correctly
self.assertEqual(employee_obj['details']['position'], 'Developer') self.assertEqual(employee_obj['details']['position'], 'Developer')
def test_embedded_update_after_save(self): def test_embedded_update_after_save(self):
"""Test update of `EmbeddedDocumentField` attached to a newly """
saved document. Test update of `EmbeddedDocumentField` attached to a newly saved
document.
""" """
class Page(EmbeddedDocument): class Page(EmbeddedDocument):
log_message = StringField(verbose_name="Log message", log_message = StringField(verbose_name="Log message",
@@ -1695,8 +1744,8 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(site.page.log_message, "Error: Dummy message") self.assertEqual(site.page.log_message, "Error: Dummy message")
def test_updating_an_embedded_document(self): def test_updating_an_embedded_document(self):
"""Ensure that a document with an embedded document field may """Ensure that a document with an embedded document field may be
be saved in the database. saved in the database.
""" """
class EmployeeDetails(EmbeddedDocument): class EmployeeDetails(EmbeddedDocument):
position = StringField() position = StringField()
@@ -1731,6 +1780,7 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(promoted_employee.details, None) self.assertEqual(promoted_employee.details, None)
def test_object_mixins(self): def test_object_mixins(self):
class NameMixin(object): class NameMixin(object):
name = StringField() name = StringField()
@@ -1769,9 +1819,9 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(t.count, 12) self.assertEqual(t.count, 12)
def test_save_reference(self): def test_save_reference(self):
"""Ensure that a document reference field may be saved in the """Ensure that a document reference field may be saved in the database.
database.
""" """
class BlogPost(Document): class BlogPost(Document):
meta = {'collection': 'blogpost_1'} meta = {'collection': 'blogpost_1'}
content = StringField() content = StringField()
@@ -1802,6 +1852,8 @@ class InstanceTest(unittest.TestCase):
author = list(self.Person.objects(name='Test User'))[-1] author = list(self.Person.objects(name='Test User'))[-1]
self.assertEqual(author.age, 25) self.assertEqual(author.age, 25)
BlogPost.drop_collection()
def test_duplicate_db_fields_raise_invalid_document_error(self): def test_duplicate_db_fields_raise_invalid_document_error(self):
"""Ensure a InvalidDocumentError is thrown if duplicate fields """Ensure a InvalidDocumentError is thrown if duplicate fields
declare the same db_field. declare the same db_field.
@@ -1812,7 +1864,7 @@ class InstanceTest(unittest.TestCase):
name2 = StringField(db_field='name') name2 = StringField(db_field='name')
def test_invalid_son(self): def test_invalid_son(self):
"""Raise an error if loading invalid data.""" """Raise an error if loading invalid data"""
class Occurrence(EmbeddedDocument): class Occurrence(EmbeddedDocument):
number = IntField() number = IntField()
@@ -1835,9 +1887,9 @@ class InstanceTest(unittest.TestCase):
Word._from_son('this is not a valid SON dict') Word._from_son('this is not a valid SON dict')
def test_reverse_delete_rule_cascade_and_nullify(self): def test_reverse_delete_rule_cascade_and_nullify(self):
"""Ensure that a referenced document is also deleted upon """Ensure that a referenced document is also deleted upon deletion.
deletion.
""" """
class BlogPost(Document): class BlogPost(Document):
content = StringField() content = StringField()
author = ReferenceField(self.Person, reverse_delete_rule=CASCADE) author = ReferenceField(self.Person, reverse_delete_rule=CASCADE)
@@ -1892,8 +1944,7 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(Book.objects.count(), 0) self.assertEqual(Book.objects.count(), 0)
def test_reverse_delete_rule_with_shared_id_among_collections(self): def test_reverse_delete_rule_with_shared_id_among_collections(self):
"""Ensure that cascade delete rule doesn't mix id among """Ensure that cascade delete rule doesn't mix id among collections.
collections.
""" """
class User(Document): class User(Document):
id = IntField(primary_key=True) id = IntField(primary_key=True)
@@ -1924,9 +1975,10 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(Book.objects.get(), book_2) self.assertEqual(Book.objects.get(), book_2)
def test_reverse_delete_rule_with_document_inheritance(self): def test_reverse_delete_rule_with_document_inheritance(self):
"""Ensure that a referenced document is also deleted upon """Ensure that a referenced document is also deleted upon deletion
deletion of a child document. of a child document.
""" """
class Writer(self.Person): class Writer(self.Person):
pass pass
@@ -1958,9 +2010,10 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(BlogPost.objects.count(), 0) self.assertEqual(BlogPost.objects.count(), 0)
def test_reverse_delete_rule_cascade_and_nullify_complex_field(self): def test_reverse_delete_rule_cascade_and_nullify_complex_field(self):
"""Ensure that a referenced document is also deleted upon """Ensure that a referenced document is also deleted upon deletion for
deletion for complex fields. complex fields.
""" """
class BlogPost(Document): class BlogPost(Document):
content = StringField() content = StringField()
authors = ListField(ReferenceField( authors = ListField(ReferenceField(
@@ -1969,6 +2022,7 @@ class InstanceTest(unittest.TestCase):
self.Person, reverse_delete_rule=NULLIFY)) self.Person, reverse_delete_rule=NULLIFY))
self.Person.drop_collection() self.Person.drop_collection()
BlogPost.drop_collection() BlogPost.drop_collection()
author = self.Person(name='Test User') author = self.Person(name='Test User')
@@ -1992,10 +2046,10 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(BlogPost.objects.count(), 0) self.assertEqual(BlogPost.objects.count(), 0)
def test_reverse_delete_rule_cascade_triggers_pre_delete_signal(self): def test_reverse_delete_rule_cascade_triggers_pre_delete_signal(self):
"""Ensure the pre_delete signal is triggered upon a cascading """ ensure the pre_delete signal is triggered upon a cascading deletion
deletion setup a blog post with content, an author and editor setup a blog post with content, an author and editor
delete the author which triggers deletion of blogpost via delete the author which triggers deletion of blogpost via cascade
cascade blog post's pre_delete signal alters an editor attribute. blog post's pre_delete signal alters an editor attribute
""" """
class Editor(self.Person): class Editor(self.Person):
review_queue = IntField(default=0) review_queue = IntField(default=0)
@@ -2023,7 +2077,6 @@ class InstanceTest(unittest.TestCase):
# delete the author, the post is also deleted due to the CASCADE rule # delete the author, the post is also deleted due to the CASCADE rule
author.delete() author.delete()
# the pre-delete signal should have decremented the editor's queue # the pre-delete signal should have decremented the editor's queue
editor = Editor.objects(name='Max P.').get() editor = Editor.objects(name='Max P.').get()
self.assertEqual(editor.review_queue, 0) self.assertEqual(editor.review_queue, 0)
@@ -2032,6 +2085,7 @@ class InstanceTest(unittest.TestCase):
"""Ensure that Bi-Directional relationships work with """Ensure that Bi-Directional relationships work with
reverse_delete_rule reverse_delete_rule
""" """
class Bar(Document): class Bar(Document):
content = StringField() content = StringField()
foo = ReferenceField('Foo') foo = ReferenceField('Foo')
@@ -2077,8 +2131,8 @@ class InstanceTest(unittest.TestCase):
mother = ReferenceField('Person', reverse_delete_rule=DENY) mother = ReferenceField('Person', reverse_delete_rule=DENY)
def test_reverse_delete_rule_cascade_recurs(self): def test_reverse_delete_rule_cascade_recurs(self):
"""Ensure that a chain of documents is also deleted upon """Ensure that a chain of documents is also deleted upon cascaded
cascaded deletion. deletion.
""" """
class BlogPost(Document): class BlogPost(Document):
content = StringField() content = StringField()
@@ -2108,10 +2162,15 @@ class InstanceTest(unittest.TestCase):
author.delete() author.delete()
self.assertEqual(Comment.objects.count(), 0) self.assertEqual(Comment.objects.count(), 0)
self.Person.drop_collection()
BlogPost.drop_collection()
Comment.drop_collection()
def test_reverse_delete_rule_deny(self): def test_reverse_delete_rule_deny(self):
"""Ensure that a document cannot be referenced if there are """Ensure that a document cannot be referenced if there are still
still documents referring to it. documents referring to it.
""" """
class BlogPost(Document): class BlogPost(Document):
content = StringField() content = StringField()
author = ReferenceField(self.Person, reverse_delete_rule=DENY) author = ReferenceField(self.Person, reverse_delete_rule=DENY)
@@ -2139,7 +2198,11 @@ class InstanceTest(unittest.TestCase):
author.delete() author.delete()
self.assertEqual(self.Person.objects.count(), 1) self.assertEqual(self.Person.objects.count(), 1)
self.Person.drop_collection()
BlogPost.drop_collection()
def subclasses_and_unique_keys_works(self): def subclasses_and_unique_keys_works(self):
class A(Document): class A(Document):
pass pass
@@ -2155,9 +2218,12 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(A.objects.count(), 2) self.assertEqual(A.objects.count(), 2)
self.assertEqual(B.objects.count(), 1) self.assertEqual(B.objects.count(), 1)
A.drop_collection()
B.drop_collection()
def test_document_hash(self): def test_document_hash(self):
"""Test document in list, dict, set.""" """Test document in list, dict, set
"""
class User(Document): class User(Document):
pass pass
@@ -2200,9 +2266,11 @@ class InstanceTest(unittest.TestCase):
# in Set # in Set
all_user_set = set(User.objects.all()) all_user_set = set(User.objects.all())
self.assertTrue(u1 in all_user_set) self.assertTrue(u1 in all_user_set)
def test_picklable(self): def test_picklable(self):
pickle_doc = PickleTest(number=1, string="One", lists=['1', '2']) pickle_doc = PickleTest(number=1, string="One", lists=['1', '2'])
pickle_doc.embedded = PickleEmbedded() pickle_doc.embedded = PickleEmbedded()
pickled_doc = pickle.dumps(pickle_doc) # make sure pickling works even before the doc is saved pickled_doc = pickle.dumps(pickle_doc) # make sure pickling works even before the doc is saved
@@ -2228,6 +2296,7 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(pickle_doc.lists, ["1", "2", "3"]) self.assertEqual(pickle_doc.lists, ["1", "2", "3"])
def test_regular_document_pickle(self): def test_regular_document_pickle(self):
pickle_doc = PickleTest(number=1, string="One", lists=['1', '2']) pickle_doc = PickleTest(number=1, string="One", lists=['1', '2'])
pickled_doc = pickle.dumps(pickle_doc) # make sure pickling works even before the doc is saved pickled_doc = pickle.dumps(pickle_doc) # make sure pickling works even before the doc is saved
pickle_doc.save() pickle_doc.save()
@@ -2250,6 +2319,7 @@ class InstanceTest(unittest.TestCase):
fixtures.PickleTest = PickleTest fixtures.PickleTest = PickleTest
def test_dynamic_document_pickle(self): def test_dynamic_document_pickle(self):
pickle_doc = PickleDynamicTest( pickle_doc = PickleDynamicTest(
name="test", number=1, string="One", lists=['1', '2']) name="test", number=1, string="One", lists=['1', '2'])
pickle_doc.embedded = PickleDynamicEmbedded(foo="Bar") pickle_doc.embedded = PickleDynamicEmbedded(foo="Bar")
@@ -2288,6 +2358,7 @@ class InstanceTest(unittest.TestCase):
validate = DictField() validate = DictField()
def test_mutating_documents(self): def test_mutating_documents(self):
class B(EmbeddedDocument): class B(EmbeddedDocument):
field1 = StringField(default='field1') field1 = StringField(default='field1')
@@ -2295,7 +2366,6 @@ class InstanceTest(unittest.TestCase):
b = EmbeddedDocumentField(B, default=lambda: B()) b = EmbeddedDocumentField(B, default=lambda: B())
A.drop_collection() A.drop_collection()
a = A() a = A()
a.save() a.save()
a.reload() a.reload()
@@ -2319,13 +2389,12 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(a.b.field2.c_field, 'new value') self.assertEqual(a.b.field2.c_field, 'new value')
def test_can_save_false_values(self): def test_can_save_false_values(self):
"""Ensures you can save False values on save.""" """Ensures you can save False values on save"""
class Doc(Document): class Doc(Document):
foo = StringField() foo = StringField()
archived = BooleanField(default=False, required=True) archived = BooleanField(default=False, required=True)
Doc.drop_collection() Doc.drop_collection()
d = Doc() d = Doc()
d.save() d.save()
d.archived = False d.archived = False
@@ -2334,12 +2403,11 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(Doc.objects(archived=False).count(), 1) self.assertEqual(Doc.objects(archived=False).count(), 1)
def test_can_save_false_values_dynamic(self): def test_can_save_false_values_dynamic(self):
"""Ensures you can save False values on dynamic docs.""" """Ensures you can save False values on dynamic docs"""
class Doc(DynamicDocument): class Doc(DynamicDocument):
foo = StringField() foo = StringField()
Doc.drop_collection() Doc.drop_collection()
d = Doc() d = Doc()
d.save() d.save()
d.archived = False d.archived = False
@@ -2379,7 +2447,7 @@ class InstanceTest(unittest.TestCase):
Collection.update = orig_update Collection.update = orig_update
def test_db_alias_tests(self): def test_db_alias_tests(self):
"""DB Alias tests.""" """ DB Alias tests """
# mongoenginetest - Is default connection alias from setUp() # mongoenginetest - Is default connection alias from setUp()
# Register Aliases # Register Aliases
register_connection('testdb-1', 'mongoenginetest2') register_connection('testdb-1', 'mongoenginetest2')
@@ -2441,7 +2509,8 @@ class InstanceTest(unittest.TestCase):
get_db("testdb-3")[AuthorBooks._get_collection_name()]) get_db("testdb-3")[AuthorBooks._get_collection_name()])
def test_db_alias_overrides(self): def test_db_alias_overrides(self):
"""Test db_alias can be overriden.""" """db_alias can be overriden
"""
# Register a connection with db_alias testdb-2 # Register a connection with db_alias testdb-2
register_connection('testdb-2', 'mongoenginetest2') register_connection('testdb-2', 'mongoenginetest2')
@@ -2465,7 +2534,8 @@ class InstanceTest(unittest.TestCase):
B._get_collection().database.name) B._get_collection().database.name)
def test_db_alias_propagates(self): def test_db_alias_propagates(self):
"""db_alias propagates?""" """db_alias propagates?
"""
register_connection('testdb-1', 'mongoenginetest2') register_connection('testdb-1', 'mongoenginetest2')
class A(Document): class A(Document):
@@ -2478,7 +2548,8 @@ class InstanceTest(unittest.TestCase):
self.assertEqual('testdb-1', B._meta.get('db_alias')) self.assertEqual('testdb-1', B._meta.get('db_alias'))
def test_db_ref_usage(self): def test_db_ref_usage(self):
"""DB Ref usage in dict_fields.""" """ DB Ref usage in dict_fields"""
class User(Document): class User(Document):
name = StringField() name = StringField()
@@ -2713,6 +2784,7 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(user.thing._data['data'], [1, 2, 3]) self.assertEqual(user.thing._data['data'], [1, 2, 3])
def test_spaces_in_keys(self): def test_spaces_in_keys(self):
class Embedded(DynamicEmbeddedDocument): class Embedded(DynamicEmbeddedDocument):
pass pass
@@ -2801,6 +2873,7 @@ class InstanceTest(unittest.TestCase):
log.machine = "127.0.0.1" log.machine = "127.0.0.1"
def test_kwargs_simple(self): def test_kwargs_simple(self):
class Embedded(EmbeddedDocument): class Embedded(EmbeddedDocument):
name = StringField() name = StringField()
@@ -2820,6 +2893,7 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(classic_doc._data, dict_doc._data) self.assertEqual(classic_doc._data, dict_doc._data)
def test_kwargs_complex(self): def test_kwargs_complex(self):
class Embedded(EmbeddedDocument): class Embedded(EmbeddedDocument):
name = StringField() name = StringField()
@@ -2842,35 +2916,36 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(classic_doc._data, dict_doc._data) self.assertEqual(classic_doc._data, dict_doc._data)
def test_positional_creation(self): def test_positional_creation(self):
"""Ensure that document may be created using positional arguments.""" """Ensure that document may be created using positional arguments.
"""
person = self.Person("Test User", 42) person = self.Person("Test User", 42)
self.assertEqual(person.name, "Test User") self.assertEqual(person.name, "Test User")
self.assertEqual(person.age, 42) self.assertEqual(person.age, 42)
def test_mixed_creation(self): def test_mixed_creation(self):
"""Ensure that document may be created using mixed arguments.""" """Ensure that document may be created using mixed arguments.
"""
person = self.Person("Test User", age=42) person = self.Person("Test User", age=42)
self.assertEqual(person.name, "Test User") self.assertEqual(person.name, "Test User")
self.assertEqual(person.age, 42) self.assertEqual(person.age, 42)
def test_positional_creation_embedded(self): def test_positional_creation_embedded(self):
"""Ensure that embedded document may be created using positional """Ensure that embedded document may be created using positional arguments.
arguments.
""" """
job = self.Job("Test Job", 4) job = self.Job("Test Job", 4)
self.assertEqual(job.name, "Test Job") self.assertEqual(job.name, "Test Job")
self.assertEqual(job.years, 4) self.assertEqual(job.years, 4)
def test_mixed_creation_embedded(self): def test_mixed_creation_embedded(self):
"""Ensure that embedded document may be created using mixed """Ensure that embedded document may be created using mixed arguments.
arguments.
""" """
job = self.Job("Test Job", years=4) job = self.Job("Test Job", years=4)
self.assertEqual(job.name, "Test Job") self.assertEqual(job.name, "Test Job")
self.assertEqual(job.years, 4) self.assertEqual(job.years, 4)
def test_mixed_creation_dynamic(self): def test_mixed_creation_dynamic(self):
"""Ensure that document may be created using mixed arguments.""" """Ensure that document may be created using mixed arguments.
"""
class Person(DynamicDocument): class Person(DynamicDocument):
name = StringField() name = StringField()
@@ -2879,14 +2954,14 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(person.age, 42) self.assertEqual(person.age, 42)
def test_bad_mixed_creation(self): def test_bad_mixed_creation(self):
"""Ensure that document gives correct error when duplicating """Ensure that document gives correct error when duplicating arguments
arguments.
""" """
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
return self.Person("Test User", 42, name="Bad User") return self.Person("Test User", 42, name="Bad User")
def test_data_contains_id_field(self): def test_data_contains_id_field(self):
"""Ensure that asking for _data returns 'id'.""" """Ensure that asking for _data returns 'id'
"""
class Person(Document): class Person(Document):
name = StringField() name = StringField()
@@ -2898,6 +2973,7 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(person._data.get('id'), person.id) self.assertEqual(person._data.get('id'), person.id)
def test_complex_nesting_document_and_embedded_document(self): def test_complex_nesting_document_and_embedded_document(self):
class Macro(EmbeddedDocument): class Macro(EmbeddedDocument):
value = DynamicField(default="UNDEFINED") value = DynamicField(default="UNDEFINED")
@@ -2940,6 +3016,7 @@ class InstanceTest(unittest.TestCase):
system.nodes["node"].parameters["param"].macros["test"].value) system.nodes["node"].parameters["param"].macros["test"].value)
def test_embedded_document_equality(self): def test_embedded_document_equality(self):
class Test(Document): class Test(Document):
field = StringField(required=True) field = StringField(required=True)
@@ -3125,7 +3202,8 @@ class InstanceTest(unittest.TestCase):
self.assertEqual(idx, 2) self.assertEqual(idx, 2)
def test_falsey_pk(self): def test_falsey_pk(self):
"""Ensure that we can create and update a document with Falsey PK.""" """Ensure that we can create and update a document with Falsey PK.
"""
class Person(Document): class Person(Document):
age = IntField(primary_key=True) age = IntField(primary_key=True)
height = FloatField() height = FloatField()

View File

@@ -5,11 +5,8 @@ import uuid
import math import math
import itertools import itertools
import re import re
import pymongo
import sys
from nose.plugins.skip import SkipTest from nose.plugins.skip import SkipTest
from collections import OrderedDict
import six import six
try: try:
@@ -28,12 +25,9 @@ except ImportError:
from mongoengine import * from mongoengine import *
from mongoengine.connection import get_db from mongoengine.connection import get_db
from mongoengine.base import (BaseDict, BaseField, EmbeddedDocumentList, from mongoengine.base import (BaseDict, BaseField, EmbeddedDocumentList,
_document_registry, TopLevelDocumentMetaclass) _document_registry)
from tests.utils import MongoDBTestCase, MONGO_TEST_DB from tests.utils import MongoDBTestCase
from mongoengine.python_support import IS_PYMONGO_3
if IS_PYMONGO_3:
from bson import CodecOptions
__all__ = ("FieldTest", "EmbeddedDocumentListFieldTestCase") __all__ = ("FieldTest", "EmbeddedDocumentListFieldTestCase")
@@ -343,6 +337,8 @@ class FieldTest(MongoDBTestCase):
class Link(Document): class Link(Document):
url = URLField() url = URLField()
Link.drop_collection()
link = Link() link = Link()
link.url = 'google' link.url = 'google'
self.assertRaises(ValidationError, link.validate) self.assertRaises(ValidationError, link.validate)
@@ -355,6 +351,8 @@ class FieldTest(MongoDBTestCase):
class Link(Document): class Link(Document):
url = URLField() url = URLField()
Link.drop_collection()
link = Link() link = Link()
link.url = u'http://привет.com' link.url = u'http://привет.com'
@@ -1175,14 +1173,6 @@ class FieldTest(MongoDBTestCase):
post.reload() post.reload()
self.assertEqual(post.info, ['0', '1', '2', '3', 'a', '5']) self.assertEqual(post.info, ['0', '1', '2', '3', 'a', '5'])
# __setitem__(index, value) with a negative index
reset_post()
post.info[-2] = 'a'
self.assertEqual(post.info, ['0', '1', '2', '3', 'a', '5'])
post.save()
post.reload()
self.assertEqual(post.info, ['0', '1', '2', '3', 'a', '5'])
# '__setitem__(slice(i, j), listB)' # '__setitem__(slice(i, j), listB)'
# aka 'listA[i:j] = listB' # aka 'listA[i:j] = listB'
# aka 'setitem(listA, slice(i, j), listB)' # aka 'setitem(listA, slice(i, j), listB)'
@@ -1193,16 +1183,6 @@ class FieldTest(MongoDBTestCase):
post.reload() post.reload()
self.assertEqual(post.info, ['0', 'h', 'e', 'l', 'l', 'o', '3', '4', '5']) self.assertEqual(post.info, ['0', 'h', 'e', 'l', 'l', 'o', '3', '4', '5'])
# '__setitem__(slice(i, j), listB)' with negative i and j
reset_post()
post.info[-5:-3] = ['h', 'e', 'l', 'l', 'o']
self.assertEqual(post.info, ['0', 'h', 'e', 'l', 'l', 'o', '3', '4', '5'])
post.save()
post.reload()
self.assertEqual(post.info, ['0', 'h', 'e', 'l', 'l', 'o', '3', '4', '5'])
# negative
# 'append' # 'append'
reset_post() reset_post()
post.info.append('h') post.info.append('h')
@@ -1903,51 +1883,6 @@ class FieldTest(MongoDBTestCase):
doc = self.db.test.find_one() doc = self.db.test.find_one()
self.assertEqual(doc['x']['i'], 2) self.assertEqual(doc['x']['i'], 2)
def test_double_embedded_db_field(self):
"""Make sure multiple layers of embedded docs resolve db fields
properly and can be initialized using dicts.
"""
class C(EmbeddedDocument):
txt = StringField()
class B(EmbeddedDocument):
c = EmbeddedDocumentField(C, db_field='fc')
class A(Document):
b = EmbeddedDocumentField(B, db_field='fb')
a = A(
b=B(
c=C(txt='hi')
)
)
a.validate()
a = A(b={'c': {'txt': 'hi'}})
a.validate()
def test_double_embedded_db_field_from_son(self):
"""Make sure multiple layers of embedded docs resolve db fields
from SON properly.
"""
class C(EmbeddedDocument):
txt = StringField()
class B(EmbeddedDocument):
c = EmbeddedDocumentField(C, db_field='fc')
class A(Document):
b = EmbeddedDocumentField(B, db_field='fb')
a = A._from_son(SON([
('fb', SON([
('fc', SON([
('txt', 'hi')
]))
]))
]))
self.assertEqual(a.b.c.txt, 'hi')
def test_embedded_document_validation(self): def test_embedded_document_validation(self):
"""Ensure that invalid embedded documents cannot be assigned to """Ensure that invalid embedded documents cannot be assigned to
embedded document fields. embedded document fields.
@@ -2090,12 +2025,6 @@ class FieldTest(MongoDBTestCase):
post1.author = post2 post1.author = post2
self.assertRaises(ValidationError, post1.validate) self.assertRaises(ValidationError, post1.validate)
# Ensure ObjectID's are accepted as references
user_object_id = user.pk
post3 = BlogPost(content="Chips and curry sauce taste good.")
post3.author = user_object_id
post3.save()
# Make sure referencing a saved document of the right type works # Make sure referencing a saved document of the right type works
user.save() user.save()
post1.author = user post1.author = user
@@ -2106,20 +2035,6 @@ class FieldTest(MongoDBTestCase):
post1.author = post2 post1.author = post2
self.assertRaises(ValidationError, post1.validate) self.assertRaises(ValidationError, post1.validate)
def test_objectid_reference_fields(self):
"""Make sure storing Object ID references works."""
class Person(Document):
name = StringField()
parent = ReferenceField('self')
Person.drop_collection()
p1 = Person(name="John").save()
Person(name="Ross", parent=p1.pk).save()
p = Person.objects.get(name="Ross")
self.assertEqual(p.parent, p1)
def test_dbref_reference_fields(self): def test_dbref_reference_fields(self):
"""Make sure storing references as bson.dbref.DBRef works.""" """Make sure storing references as bson.dbref.DBRef works."""
class Person(Document): class Person(Document):
@@ -3453,99 +3368,23 @@ class FieldTest(MongoDBTestCase):
class User(Document): class User(Document):
email = EmailField() email = EmailField()
user = User(email='ross@example.com') user = User(email="ross@example.com")
user.validate() self.assertTrue(user.validate() is None)
user = User(email='ross@example.co.uk') user = User(email="ross@example.co.uk")
user.validate() self.assertTrue(user.validate() is None)
user = User(email=('Kofq@rhom0e4klgauOhpbpNdogawnyIKvQS0wk2mjqrgGQ5S' user = User(email=("Kofq@rhom0e4klgauOhpbpNdogawnyIKvQS0wk2mjqrgGQ5S"
'aJIazqqWkm7.net')) "aJIazqqWkm7.net"))
user.validate() self.assertTrue(user.validate() is None)
user = User(email='new-tld@example.technology') user = User(email="new-tld@example.technology")
user.validate() self.assertTrue(user.validate() is None)
user = User(email='ross@example.com.')
self.assertRaises(ValidationError, user.validate)
# unicode domain
user = User(email=u'user@пример.рф')
user.validate()
# invalid unicode domain
user = User(email=u'user@пример')
self.assertRaises(ValidationError, user.validate)
# invalid data type
user = User(email=123)
self.assertRaises(ValidationError, user.validate)
def test_email_field_unicode_user(self):
# Don't run this test on pypy3, which doesn't support unicode regex:
# https://bitbucket.org/pypy/pypy/issues/1821/regular-expression-doesnt-find-unicode
if sys.version_info[:2] == (3, 2):
raise SkipTest('unicode email addresses are not supported on PyPy 3')
class User(Document):
email = EmailField()
# unicode user shouldn't validate by default...
user = User(email=u'Dörte@Sörensen.example.com')
self.assertRaises(ValidationError, user.validate)
# ...but it should be fine with allow_utf8_user set to True
class User(Document):
email = EmailField(allow_utf8_user=True)
user = User(email=u'Dörte@Sörensen.example.com')
user.validate()
def test_email_field_domain_whitelist(self):
class User(Document):
email = EmailField()
# localhost domain shouldn't validate by default...
user = User(email='me@localhost')
self.assertRaises(ValidationError, user.validate)
# ...but it should be fine if it's whitelisted
class User(Document):
email = EmailField(domain_whitelist=['localhost'])
user = User(email='me@localhost') user = User(email='me@localhost')
user.validate()
def test_email_field_ip_domain(self):
class User(Document):
email = EmailField()
valid_ipv4 = 'email@[127.0.0.1]'
valid_ipv6 = 'email@[2001:dB8::1]'
invalid_ip = 'email@[324.0.0.1]'
# IP address as a domain shouldn't validate by default...
user = User(email=valid_ipv4)
self.assertRaises(ValidationError, user.validate) self.assertRaises(ValidationError, user.validate)
user = User(email=valid_ipv6) user = User(email="ross@example.com.")
self.assertRaises(ValidationError, user.validate)
user = User(email=invalid_ip)
self.assertRaises(ValidationError, user.validate)
# ...but it should be fine with allow_ip_domain set to True
class User(Document):
email = EmailField(allow_ip_domain=True)
user = User(email=valid_ipv4)
user.validate()
user = User(email=valid_ipv6)
user.validate()
# invalid IP should still fail validation
user = User(email=invalid_ip)
self.assertRaises(ValidationError, user.validate) self.assertRaises(ValidationError, user.validate)
def test_email_field_honors_regex(self): def test_email_field_honors_regex(self):
@@ -4188,67 +4027,6 @@ class EmbeddedDocumentListFieldTestCase(MongoDBTestCase):
self.assertTrue(hasattr(CustomData.c_field, 'custom_data')) self.assertTrue(hasattr(CustomData.c_field, 'custom_data'))
self.assertEqual(custom_data['a'], CustomData.c_field.custom_data['a']) self.assertEqual(custom_data['a'], CustomData.c_field.custom_data['a'])
def test_dynamicfield_with_container_class(self):
"""
Tests that object can be stored in order by DynamicField class
with container_class parameter.
"""
raw_data = [('d', 1), ('c', 2), ('b', 3), ('a', 4)]
class Doc(Document):
ordered_data = DynamicField(container_class=OrderedDict)
unordered_data = DynamicField()
Doc.drop_collection()
doc = Doc(ordered_data=OrderedDict(raw_data), unordered_data=dict(raw_data)).save()
# checks that the data is in order
self.assertEqual(type(doc.ordered_data), OrderedDict)
self.assertEqual(type(doc.unordered_data), dict)
self.assertEqual(','.join(doc.ordered_data.keys()), 'd,c,b,a')
# checks that the data is stored to the database in order
pymongo_db = pymongo.MongoClient()[MONGO_TEST_DB]
if IS_PYMONGO_3:
codec_option = CodecOptions(document_class=OrderedDict)
db_doc = pymongo_db.doc.with_options(codec_options=codec_option).find_one()
else:
db_doc = pymongo_db.doc.find_one(as_class=OrderedDict)
self.assertEqual(','.join(doc.ordered_data.keys()), 'd,c,b,a')
def test_dynamicfield_with_wrong_container_class(self):
with self.assertRaises(ValidationError):
class DocWithInvalidField:
data = DynamicField(container_class=list)
def test_dynamicfield_with_wrong_container_class_and_reload_docuemnt(self):
# This is because 'codec_options' is supported on pymongo3 or later
if IS_PYMONGO_3:
class OrderedDocument(Document):
my_metaclass = TopLevelDocumentMetaclass
__metaclass__ = TopLevelDocumentMetaclass
@classmethod
def _get_collection(cls):
collection = super(OrderedDocument, cls)._get_collection()
opts = CodecOptions(document_class=OrderedDict)
return collection.with_options(codec_options=opts)
raw_data = [('d', 1), ('c', 2), ('b', 3), ('a', 4)]
class Doc(OrderedDocument):
data = DynamicField(container_class=OrderedDict)
Doc.drop_collection()
doc = Doc(data=OrderedDict(raw_data)).save()
doc.reload()
self.assertEqual(type(doc.data), OrderedDict)
self.assertEqual(','.join(doc.data.keys()), 'd,c,b,a')
class CachedReferenceFieldTest(MongoDBTestCase): class CachedReferenceFieldTest(MongoDBTestCase):

View File

@@ -4962,6 +4962,20 @@ class QuerySetTest(unittest.TestCase):
for p in Person.objects(): for p in Person.objects():
self.assertEqual(p.name, 'a') self.assertEqual(p.name, 'a')
def test_last_field_name_like_operator(self):
class EmbeddedItem(EmbeddedDocument):
type = StringField()
class Doc(Document):
item = EmbeddedDocumentField(EmbeddedItem)
Doc.drop_collection()
doc = Doc(item=EmbeddedItem(type="axe"))
doc.save()
self.assertEqual(1, Doc.objects(item__type__="axe").count())
def test_len_during_iteration(self): def test_len_during_iteration(self):
"""Tests that calling len on a queyset during iteration doesn't """Tests that calling len on a queyset during iteration doesn't
stop paging. stop paging.

View File

@@ -2,15 +2,10 @@
import unittest import unittest
from bson import DBRef, ObjectId from bson import DBRef, ObjectId
from collections import OrderedDict
from mongoengine import * from mongoengine import *
from mongoengine.connection import get_db from mongoengine.connection import get_db
from mongoengine.context_managers import query_counter from mongoengine.context_managers import query_counter
from mongoengine.python_support import IS_PYMONGO_3
from mongoengine.base import TopLevelDocumentMetaclass
if IS_PYMONGO_3:
from bson import CodecOptions
class FieldTest(unittest.TestCase): class FieldTest(unittest.TestCase):
@@ -1292,70 +1287,5 @@ class FieldTest(unittest.TestCase):
self.assertEqual(q, 2) self.assertEqual(q, 2)
def test_dynamic_field_dereference(self):
class Merchandise(Document):
name = StringField()
price = IntField()
class Store(Document):
merchandises = DynamicField()
Merchandise.drop_collection()
Store.drop_collection()
merchandises = {
'#1': Merchandise(name='foo', price=100).save(),
'#2': Merchandise(name='bar', price=120).save(),
'#3': Merchandise(name='baz', price=110).save(),
}
Store(merchandises=merchandises).save()
store = Store.objects().first()
for obj in store.merchandises.values():
self.assertFalse(isinstance(obj, Merchandise))
store.select_related()
for obj in store.merchandises.values():
self.assertTrue(isinstance(obj, Merchandise))
def test_dynamic_field_dereference_with_ordering_guarantee_on_pymongo3(self):
# This is because 'codec_options' is supported on pymongo3 or later
if IS_PYMONGO_3:
class OrderedDocument(Document):
my_metaclass = TopLevelDocumentMetaclass
__metaclass__ = TopLevelDocumentMetaclass
@classmethod
def _get_collection(cls):
collection = super(OrderedDocument, cls)._get_collection()
opts = CodecOptions(document_class=OrderedDict)
return collection.with_options(codec_options=opts)
class Merchandise(Document):
name = StringField()
price = IntField()
class Store(OrderedDocument):
merchandises = DynamicField(container_class=OrderedDict)
Merchandise.drop_collection()
Store.drop_collection()
merchandises = OrderedDict()
merchandises['#1'] = Merchandise(name='foo', price=100).save()
merchandises['#2'] = Merchandise(name='bar', price=120).save()
merchandises['#3'] = Merchandise(name='baz', price=110).save()
Store(merchandises=merchandises).save()
store = Store.objects().first()
store.select_related()
# confirms that the load data order is same with the one at storing
self.assertTrue(type(store.merchandises), OrderedDict)
self.assertEqual(','.join(store.merchandises.keys()), '#1,#2,#3')
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()