From 2e18199eb2cad168f07caefd68f4cdd5f3078135 Mon Sep 17 00:00:00 2001 From: hellysmile Date: Fri, 1 Feb 2013 04:17:16 +0200 Subject: [PATCH 01/37] Django sessions TTL support --- docs/django.rst | 3 +++ mongoengine/django/sessions.py | 14 +++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/docs/django.rst b/docs/django.rst index 144baab5..58eadc60 100644 --- a/docs/django.rst +++ b/docs/django.rst @@ -45,6 +45,9 @@ into you settings module:: SESSION_ENGINE = 'mongoengine.django.sessions' +Django provides session cookie, which expires after ```SESSION_COOKIE_AGE``` seconds, but doesnt delete cookie at sessions backend, so ```'mongoengine.django.sessions'``` supports `mongodb TTL +`_. + .. versionadded:: 0.2.1 Storage diff --git a/mongoengine/django/sessions.py b/mongoengine/django/sessions.py index 810b6265..20e6b62e 100644 --- a/mongoengine/django/sessions.py +++ b/mongoengine/django/sessions.py @@ -31,9 +31,17 @@ class MongoSession(Document): else fields.DictField() expire_date = fields.DateTimeField() - meta = {'collection': MONGOENGINE_SESSION_COLLECTION, - 'db_alias': MONGOENGINE_SESSION_DB_ALIAS, - 'allow_inheritance': False} + meta = { + 'collection': MONGOENGINE_SESSION_COLLECTION, + 'db_alias': MONGOENGINE_SESSION_DB_ALIAS, + 'allow_inheritance': False, + 'indexes': [ + { + 'fields': ['expire_date'], + 'expireAfterSeconds': settings.SESSION_COOKIE_AGE + } + ] + } class SessionStore(SessionBase): From d6b4ca7a985c1ba4c25692d9731b8b6cd12e7fb0 Mon Sep 17 00:00:00 2001 From: hellysmile Date: Fri, 1 Feb 2013 04:19:55 +0200 Subject: [PATCH 02/37] Fix docs quotes --- docs/django.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/django.rst b/docs/django.rst index 58eadc60..b2acee72 100644 --- a/docs/django.rst +++ b/docs/django.rst @@ -45,7 +45,7 @@ into you settings module:: SESSION_ENGINE = 'mongoengine.django.sessions' -Django provides session cookie, which expires after ```SESSION_COOKIE_AGE``` seconds, but doesnt delete cookie at sessions backend, so ```'mongoengine.django.sessions'``` supports `mongodb TTL +Django provides session cookie, which expires after ```SESSION_COOKIE_AGE``` seconds, but doesnt delete cookie at sessions backend, so ``'mongoengine.django.sessions'`` supports `mongodb TTL `_. .. versionadded:: 0.2.1 From 3477b0107a227b88a1936fb23fa24ad7b26524a6 Mon Sep 17 00:00:00 2001 From: Loic Raucy Date: Tue, 26 Feb 2013 11:12:37 +0100 Subject: [PATCH 03/37] Added regression test for numerical string keys. --- tests/test_fields.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/test_fields.py b/tests/test_fields.py index 28af1b23..47669007 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -965,6 +965,24 @@ class FieldTest(unittest.TestCase): doc = self.db.test.find_one() self.assertEqual(doc['x']['DICTIONARY_KEY']['i'], 2) + def test_mapfield_numerical_index(self): + """Ensure that MapField accept numeric strings as indexes.""" + class Embedded(EmbeddedDocument): + name = StringField() + + class Test(Document): + my_map = MapField(EmbeddedDocumentField(Embedded)) + + Test.drop_collection() + + test = Test() + test.my_map['1'] = Embedded(name='test') + test.save() + test.my_map['1'].name = 'test updated' + test.save() + + Test.drop_collection() + def test_map_field_lookup(self): """Ensure MapField lookups succeed on Fields without a lookup method""" From d0245bb5ba3b0f4ca4ce654fd199c167ce8c5e96 Mon Sep 17 00:00:00 2001 From: Loic Raucy Date: Tue, 26 Feb 2013 11:14:47 +0100 Subject: [PATCH 04/37] Fixed #238: dictfields handle numerical strings indexes. --- mongoengine/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mongoengine/base.py b/mongoengine/base.py index 013afe78..4f302a87 100644 --- a/mongoengine/base.py +++ b/mongoengine/base.py @@ -1193,7 +1193,7 @@ class BaseDocument(object): for p in parts: if isinstance(d, DBRef): break - elif p.isdigit(): + elif isinstance(d, list) and p.isdigit(): d = d[int(p)] elif hasattr(d, 'get'): d = d.get(p) @@ -1224,7 +1224,7 @@ class BaseDocument(object): parts = path.split('.') db_field_name = parts.pop() for p in parts: - if p.isdigit(): + if isinstance(d, list) and p.isdigit(): d = d[int(p)] elif (hasattr(d, '__getattribute__') and not isinstance(d, dict)): From 0d2e84b16b286fd7724676794a68aa0610285afc Mon Sep 17 00:00:00 2001 From: benoitlouy Date: Thu, 28 Feb 2013 00:37:34 -0500 Subject: [PATCH 05/37] Fix for issue #237: clearing changed fields recursively in EmbeddedDocuments after saving a Document --- mongoengine/base.py | 16 ++++++++++++++++ mongoengine/document.py | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/mongoengine/base.py b/mongoengine/base.py index 013afe78..521756cd 100644 --- a/mongoengine/base.py +++ b/mongoengine/base.py @@ -1123,6 +1123,22 @@ class BaseDocument(object): key not in self._changed_fields): self._changed_fields.append(key) + def _clear_changed_fields(self): + self._changed_fields = [] + EmbeddedDocumentField = _import_class("EmbeddedDocumentField") + for field_name, field in self._fields.iteritems(): + if (isinstance(field, ComplexBaseField) and + isinstance(field.field, EmbeddedDocumentField)): + field_value = getattr(self, field_name, None) + if field_value: + for idx in (field_value if isinstance(field_value, dict) + else xrange(len(field_value))): + field_value[idx]._clear_changed_fields() + elif isinstance(field, EmbeddedDocumentField): + field_value = getattr(self, field_name, None) + if field_value: + field_value._clear_changed_fields() + def _get_changed_fields(self, key='', inspected=None): """Returns a list of all fields that have explicitly been changed. """ diff --git a/mongoengine/document.py b/mongoengine/document.py index 7b3afafb..0cf07a9e 100644 --- a/mongoengine/document.py +++ b/mongoengine/document.py @@ -269,7 +269,7 @@ class Document(BaseDocument): if id_field not in self._meta.get('shard_key', []): self[id_field] = self._fields[id_field].to_python(object_id) - self._changed_fields = [] + self._clear_changed_fields() self._created = False signals.post_save.send(self.__class__, document=self, created=created) return self From 43327ea4e1ca2041de248b438d77b392bd59f6c3 Mon Sep 17 00:00:00 2001 From: benoitlouy Date: Fri, 1 Mar 2013 07:38:28 -0500 Subject: [PATCH 06/37] Add testcase for issue #237 --- tests/test_document.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/test_document.py b/tests/test_document.py index 3e8d8134..134ba34f 100644 --- a/tests/test_document.py +++ b/tests/test_document.py @@ -3368,6 +3368,40 @@ class DocumentTest(unittest.TestCase): } ) ]), "1,2") + def test_complex_nesting_document_and_embeddeddocument(self): + class Macro(EmbeddedDocument): + value = DynamicField(default="UNDEFINED") + + class Parameter(EmbeddedDocument): + macros = MapField(EmbeddedDocumentField(Macro)) + + def expand(self): + self.macros["test"] = Macro() + + class Node(Document): + parameters = MapField(EmbeddedDocumentField(Parameter)) + + def expand(self): + self.flattened_parameter = {} + for parameter_name, parameter in self.parameters.iteritems(): + parameter.expand() + + class System(Document): + name = StringField(required=True) + nodes = MapField(ReferenceField(Node, dbref=False)) + + def save(self, *args, **kwargs): + for node_name, node in self.nodes.iteritems(): + node.expand() + node.save(*args, **kwargs) + super(System, self).save(*args, **kwargs) + + system = System(name="system") + system.save() + system.nodes["node"] = Node() + system.save() + system.nodes["node"].parameters["param"] = Parameter() + system.save() class ValidatorErrorTest(unittest.TestCase): From 41a698b442881b7f40cef5b3c6954097d0965a10 Mon Sep 17 00:00:00 2001 From: Russ Weeks Date: Tue, 12 Mar 2013 10:28:29 -0700 Subject: [PATCH 07/37] Changed dereference.py to keep tuples as tuples --- mongoengine/dereference.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mongoengine/dereference.py b/mongoengine/dereference.py index 386dbf4b..fcb6d89a 100644 --- a/mongoengine/dereference.py +++ b/mongoengine/dereference.py @@ -171,6 +171,7 @@ class DeReference(object): if not hasattr(items, 'items'): is_list = True + as_tuple = isinstance(items, tuple) iterator = enumerate(items) data = [] else: @@ -205,7 +206,7 @@ class DeReference(object): if instance and name: if is_list: - return BaseList(data, instance, name) + return tuple(data) if as_tuple else BaseList(data, instance, name) return BaseDict(data, instance, name) depth += 1 return data From f9cd8b1841631c693ebf40479d215bee4ed13d30 Mon Sep 17 00:00:00 2001 From: Russ Weeks Date: Tue, 12 Mar 2013 12:45:38 -0700 Subject: [PATCH 08/37] added unit test for dereference patch --- tests/test_dereference.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/test_dereference.py b/tests/test_dereference.py index 0eb891cb..d7438d2f 100644 --- a/tests/test_dereference.py +++ b/tests/test_dereference.py @@ -997,3 +997,34 @@ class FieldTest(unittest.TestCase): msg = Message.objects.get(id=1) self.assertEqual(0, msg.comments[0].id) self.assertEqual(1, msg.comments[1].id) + + def test_tuples_as_tuples(self): + """ + Ensure that tuples remain tuples when they are + inside a ComplexBaseField + """ + from mongoengine.base import BaseField + class EnumField(BaseField): + def __init__(self, **kwargs): + super(EnumField,self).__init__(**kwargs) + + def to_mongo(self, value): + return value + + def to_python(self, value): + return tuple(value) + + class TestDoc(Document): + items = ListField(EnumField()) + + TestDoc.drop_collection() + tuples = [(100,'Testing')] + doc = TestDoc() + doc.items = tuples + doc.save() + x = TestDoc.objects().get() + self.assertTrue(x is not None) + self.assertTrue(len(x.items) == 1) + self.assertTrue(tuple(x.items[0]) in tuples) + self.assertTrue(x.items[0] in tuples) + From 2d6ae1691204b1036ee5b7595721303a8533a103 Mon Sep 17 00:00:00 2001 From: Jaepil Jeong Date: Thu, 14 Mar 2013 23:25:22 +0900 Subject: [PATCH 09/37] Added LongField to support 64-bit integer type. --- mongoengine/fields.py | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/mongoengine/fields.py b/mongoengine/fields.py index de484a1d..c40491b6 100644 --- a/mongoengine/fields.py +++ b/mongoengine/fields.py @@ -27,7 +27,7 @@ except ImportError: Image = None ImageOps = None -__all__ = ['StringField', 'IntField', 'FloatField', 'BooleanField', +__all__ = ['StringField', 'IntField', 'LongField', 'FloatField', 'BooleanField', 'DateTimeField', 'EmbeddedDocumentField', 'ListField', 'DictField', 'ObjectIdField', 'ReferenceField', 'ValidationError', 'MapField', 'DecimalField', 'ComplexDateTimeField', 'URLField', 'DynamicField', @@ -153,7 +153,7 @@ class EmailField(StringField): class IntField(BaseField): - """An integer field. + """An 32-bit integer field. """ def __init__(self, min_value=None, max_value=None, **kwargs): @@ -186,6 +186,40 @@ class IntField(BaseField): return int(value) +class LongField(BaseField): + """An 64-bit integer field. + """ + + def __init__(self, min_value=None, max_value=None, **kwargs): + self.min_value, self.max_value = min_value, max_value + super(LongField, self).__init__(**kwargs) + + def to_python(self, value): + try: + value = long(value) + except ValueError: + pass + return value + + def validate(self, value): + try: + value = long(value) + except: + self.error('%s could not be converted to long' % value) + + if self.min_value is not None and value < self.min_value: + self.error('Long value is too small') + + if self.max_value is not None and value > self.max_value: + self.error('Long value is too large') + + def prepare_query_value(self, op, value): + if value is None: + return value + + return long(value) + + class FloatField(BaseField): """An floating point number field. """ From e9464e32db4bcd10b332fb4764b3f9189e096f36 Mon Sep 17 00:00:00 2001 From: Jaepil Jeong Date: Thu, 14 Mar 2013 23:59:50 +0900 Subject: [PATCH 10/37] Added test cases for LongField. --- tests/test_fields.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/test_fields.py b/tests/test_fields.py index 28af1b23..3ceff8d9 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -144,6 +144,17 @@ class FieldTest(unittest.TestCase): self.assertEqual(1, TestDocument.objects(int_fld__ne=None).count()) self.assertEqual(1, TestDocument.objects(float_fld__ne=None).count()) + def test_long_ne_operator(self): + class TestDocument(Document): + long_fld = LongField() + + TestDocument.drop_collection() + + TestDocument(long_fld=None).save() + TestDocument(long_fld=1).save() + + self.assertEqual(1, TestDocument.objects(long_fld__ne=None).count()) + def test_object_id_validation(self): """Ensure that invalid values cannot be assigned to string fields. """ @@ -217,6 +228,23 @@ class FieldTest(unittest.TestCase): person.age = 'ten' self.assertRaises(ValidationError, person.validate) + def test_long_validation(self): + """Ensure that invalid values cannot be assigned to long fields. + """ + class TestDocument(Document): + value = LongField(min_value=0, max_value=110) + + doc = TestDocument() + doc.value = 50 + doc.validate() + + doc.value = -1 + self.assertRaises(ValidationError, doc.validate) + doc.age = 120 + self.assertRaises(ValidationError, doc.validate) + doc.age = 'ten' + self.assertRaises(ValidationError, doc.validate) + def test_float_validation(self): """Ensure that invalid values cannot be assigned to float fields. """ From 67182713d96233d3d2feb0f67d39ddaf1789c692 Mon Sep 17 00:00:00 2001 From: Jaepil Jeong Date: Fri, 15 Mar 2013 00:12:48 +0900 Subject: [PATCH 11/37] Fixed potential overflow error. --- mongoengine/fields.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mongoengine/fields.py b/mongoengine/fields.py index c40491b6..d7c7cf1f 100644 --- a/mongoengine/fields.py +++ b/mongoengine/fields.py @@ -213,6 +213,9 @@ class LongField(BaseField): if self.max_value is not None and value > self.max_value: self.error('Long value is too large') + if value > 0x7FFFFFFFFFFFFFFF: + self.error('Long value is too large') + def prepare_query_value(self, op, value): if value is None: return value From a762a10decef552ccf6fbee413819d18bede05ea Mon Sep 17 00:00:00 2001 From: Jaepil Jeong Date: Mon, 18 Mar 2013 19:30:04 +0900 Subject: [PATCH 12/37] Revert "Fixed potential overflow error." This reverts commit 67182713d96233d3d2feb0f67d39ddaf1789c692. --- mongoengine/fields.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/mongoengine/fields.py b/mongoengine/fields.py index d7c7cf1f..c40491b6 100644 --- a/mongoengine/fields.py +++ b/mongoengine/fields.py @@ -213,9 +213,6 @@ class LongField(BaseField): if self.max_value is not None and value > self.max_value: self.error('Long value is too large') - if value > 0x7FFFFFFFFFFFFFFF: - self.error('Long value is too large') - def prepare_query_value(self, op, value): if value is None: return value From faf840f924c6d8432c88c70ae949c8b14f0d72ed Mon Sep 17 00:00:00 2001 From: Paul Swartz Date: Mon, 25 Mar 2013 10:59:31 -0400 Subject: [PATCH 13/37] only mark a field as changed if the value has changed Prevents spurious changes from being recorded. --- AUTHORS | 1 + mongoengine/base.py | 14 ++++++-------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/AUTHORS b/AUTHORS index 82a1dfa4..3d05cc41 100644 --- a/AUTHORS +++ b/AUTHORS @@ -128,3 +128,4 @@ that much better: * Peter Teichman * Jakub Kot * Jorge Bastida + * Paul Swartz \ No newline at end of file diff --git a/mongoengine/base.py b/mongoengine/base.py index f73af4cc..0e46248f 100644 --- a/mongoengine/base.py +++ b/mongoengine/base.py @@ -205,8 +205,12 @@ class BaseField(object): def __set__(self, instance, value): """Descriptor for assigning a value to a field in a document. """ - instance._data[self.name] = value - if instance._initialised: + changed = False + if (self.name not in instance._data or + instance._data[self.name] != value): + changed = True + instance._data[self.name] = value + if changed and instance._initialised: instance._mark_as_changed(self.name) def error(self, message="", errors=None, field_name=None): @@ -317,12 +321,6 @@ class ComplexBaseField(BaseField): return value - def __set__(self, instance, value): - """Descriptor for assigning a value to a field in a document. - """ - instance._data[self.name] = value - instance._mark_as_changed(self.name) - def to_python(self, value): """Convert a MongoDB-compatible type to a Python type. """ From 20cb0285f004def84e83b501af35672d544afe0f Mon Sep 17 00:00:00 2001 From: Paul Swartz Date: Wed, 27 Mar 2013 14:53:47 -0400 Subject: [PATCH 14/37] explicitly check for Document instances when dereferencing In particular, `collections.namedtuple` instances also have a `_fields` attribute which confuses the dereferencing. --- mongoengine/dereference.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mongoengine/dereference.py b/mongoengine/dereference.py index 386dbf4b..b227ed36 100644 --- a/mongoengine/dereference.py +++ b/mongoengine/dereference.py @@ -33,7 +33,7 @@ class DeReference(object): self.max_depth = max_depth doc_type = None - if instance and instance._fields: + if instance and isinstance(instance, Document): doc_type = instance._fields.get(name) if hasattr(doc_type, 'field'): doc_type = doc_type.field @@ -84,7 +84,7 @@ class DeReference(object): # Recursively find dbreferences depth += 1 for k, item in iterator: - if hasattr(item, '_fields'): + if isinstance(item, Document): for field_name, field in item._fields.iteritems(): v = item._data.get(field_name, None) if isinstance(v, (DBRef)): @@ -187,7 +187,7 @@ class DeReference(object): if k in self.object_map and not is_list: data[k] = self.object_map[k] - elif hasattr(v, '_fields'): + elif isinstance(v, Document): for field_name, field in v._fields.iteritems(): v = data[k]._data.get(field_name, None) if isinstance(v, (DBRef)): From 74f3f4eb158ab5bd980578582af8454b0198577a Mon Sep 17 00:00:00 2001 From: Stefan Wojcik Date: Mon, 1 Apr 2013 16:17:17 -0700 Subject: [PATCH 15/37] more ordering unit tests --- tests/test_queryset.py | 91 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 81 insertions(+), 10 deletions(-) diff --git a/tests/test_queryset.py b/tests/test_queryset.py index 5234cea2..a87611c8 100644 --- a/tests/test_queryset.py +++ b/tests/test_queryset.py @@ -952,6 +952,11 @@ class QuerySetTest(unittest.TestCase): {'attachments.views.extracted': 'no'}]} self.assertEqual(expected, raw_query) + def assertSequence(self, qs, expected): + self.assertEqual(len(qs), len(expected)) + for i in range(len(qs)): + self.assertEqual(qs[i], expected[i]) + def test_ordering(self): """Ensure default ordering is applied and can be overridden. """ @@ -965,10 +970,10 @@ class QuerySetTest(unittest.TestCase): BlogPost.drop_collection() - blog_post_1 = BlogPost(title="Blog Post #1", - published_date=datetime(2010, 1, 5, 0, 0 ,0)) blog_post_2 = BlogPost(title="Blog Post #2", published_date=datetime(2010, 1, 6, 0, 0 ,0)) + blog_post_1 = BlogPost(title="Blog Post #1", + published_date=datetime(2010, 1, 5, 0, 0 ,0)) blog_post_3 = BlogPost(title="Blog Post #3", published_date=datetime(2010, 1, 7, 0, 0 ,0)) @@ -978,14 +983,13 @@ class QuerySetTest(unittest.TestCase): # get the "first" BlogPost using default ordering # from BlogPost.meta.ordering - latest_post = BlogPost.objects.first() - self.assertEqual(latest_post.title, "Blog Post #3") + expected = [blog_post_3, blog_post_2, blog_post_1] + self.assertSequence(BlogPost.objects.all(), expected) # override default ordering, order BlogPosts by "published_date" - first_post = BlogPost.objects.order_by("+published_date").first() - self.assertEqual(first_post.title, "Blog Post #1") - - BlogPost.drop_collection() + qs = BlogPost.objects.order_by("+published_date") + expected = [blog_post_1, blog_post_2, blog_post_3] + self.assertSequence(qs, expected) def test_only(self): """Ensure that QuerySet.only only returns the requested fields. @@ -1921,8 +1925,8 @@ class QuerySetTest(unittest.TestCase): def test_order_by(self): """Ensure that QuerySets may be ordered. """ - self.Person(name="User A", age=20).save() self.Person(name="User B", age=40).save() + self.Person(name="User A", age=20).save() self.Person(name="User C", age=30).save() names = [p.name for p in self.Person.objects.order_by('-age')] @@ -1937,11 +1941,67 @@ class QuerySetTest(unittest.TestCase): ages = [p.age for p in self.Person.objects.order_by('-name')] self.assertEqual(ages, [30, 40, 20]) + def test_order_by_optional(self): + class BlogPost(Document): + title = StringField() + published_date = DateTimeField(required=False) + + BlogPost.drop_collection() + + blog_post_3 = BlogPost(title="Blog Post #3", + published_date=datetime(2010, 1, 6, 0, 0 ,0)) + blog_post_2 = BlogPost(title="Blog Post #2", + published_date=datetime(2010, 1, 5, 0, 0 ,0)) + blog_post_4 = BlogPost(title="Blog Post #4", + published_date=datetime(2010, 1, 7, 0, 0 ,0)) + blog_post_1 = BlogPost(title="Blog Post #1", published_date=None) + + blog_post_3.save() + blog_post_1.save() + blog_post_4.save() + blog_post_2.save() + + expected = [blog_post_1, blog_post_2, blog_post_3, blog_post_4] + self.assertSequence(BlogPost.objects.order_by('published_date'), + expected) + self.assertSequence(BlogPost.objects.order_by('+published_date'), + expected) + + expected.reverse() + self.assertSequence(BlogPost.objects.order_by('-published_date'), + expected) + + def test_order_by_list(self): + class BlogPost(Document): + title = StringField() + published_date = DateTimeField(required=False) + + BlogPost.drop_collection() + + blog_post_1 = BlogPost(title="A", + published_date=datetime(2010, 1, 6, 0, 0 ,0)) + blog_post_2 = BlogPost(title="B", + published_date=datetime(2010, 1, 6, 0, 0 ,0)) + blog_post_3 = BlogPost(title="C", + published_date=datetime(2010, 1, 7, 0, 0 ,0)) + + blog_post_2.save() + blog_post_3.save() + blog_post_1.save() + + qs = BlogPost.objects.order_by('published_date', 'title') + expected = [blog_post_1, blog_post_2, blog_post_3] + self.assertSequence(qs, expected) + + qs = BlogPost.objects.order_by('-published_date', '-title') + expected.reverse() + self.assertSequence(qs, expected) + def test_order_by_chaining(self): """Ensure that an order_by query chains properly and allows .only() """ - self.Person(name="User A", age=20).save() self.Person(name="User B", age=40).save() + self.Person(name="User A", age=20).save() self.Person(name="User C", age=30).save() only_age = self.Person.objects.order_by('-age').only('age') @@ -1953,6 +2013,17 @@ class QuerySetTest(unittest.TestCase): self.assertEqual(names, [None, None, None]) self.assertEqual(ages, [40, 30, 20]) + qs = self.Person.objects.all().limit(10) + qs = qs.order_by('-age') + ages = [p.age for p in qs] + self.assertEqual(ages, [40, 30, 20]) + + qs = self.Person.objects.all().skip(0) + qs = qs.order_by('-age') + ages = [p.age for p in qs] + self.assertEqual(ages, [40, 30, 20]) + + def test_confirm_order_by_reference_wont_work(self): """Ordering by reference is not possible. Use map / reduce.. or denormalise""" From dfabfce01bd91f9104fde9cd1892f9ca58fbe41b Mon Sep 17 00:00:00 2001 From: Stefan Wojcik Date: Mon, 1 Apr 2013 17:17:01 -0700 Subject: [PATCH 16/37] show that order_by followed by limit works, but not the other way around --- tests/test_queryset.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/test_queryset.py b/tests/test_queryset.py index a87611c8..56177ec4 100644 --- a/tests/test_queryset.py +++ b/tests/test_queryset.py @@ -2013,6 +2013,11 @@ class QuerySetTest(unittest.TestCase): self.assertEqual(names, [None, None, None]) self.assertEqual(ages, [40, 30, 20]) + qs = self.Person.objects.all().order_by('-age') + qs = qs.limit(10) + ages = [p.age for p in qs] + self.assertEqual(ages, [40, 30, 20]) + qs = self.Person.objects.all().limit(10) qs = qs.order_by('-age') ages = [p.age for p in qs] @@ -2023,7 +2028,6 @@ class QuerySetTest(unittest.TestCase): ages = [p.age for p in qs] self.assertEqual(ages, [40, 30, 20]) - def test_confirm_order_by_reference_wont_work(self): """Ordering by reference is not possible. Use map / reduce.. or denormalise""" From 32d5c0c946f68a9328e23c688ad37b4472033e1c Mon Sep 17 00:00:00 2001 From: Alice Bevan-McGregor Date: Wed, 3 Apr 2013 15:00:34 -0400 Subject: [PATCH 17/37] Store ordered list of field names, and return the ordered list when iterating a document instance. --- mongoengine/base.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/mongoengine/base.py b/mongoengine/base.py index f73af4cc..2f956ccf 100644 --- a/mongoengine/base.py +++ b/mongoengine/base.py @@ -560,8 +560,11 @@ class DocumentMetaclass(type): # Set _fields and db_field maps attrs['_fields'] = doc_fields - attrs['_db_field_map'] = dict([(k, getattr(v, 'db_field', k)) - for k, v in doc_fields.iteritems()]) + attrs['_fields_ordered'] = tuple(i[1] + for i in sorted((v.creation_counter, v.name) + for v in doc_fields.itervalues())) + attrs['_db_field_map'] = dict((k, getattr(v, 'db_field', k)) + for k, v in doc_fields.iteritems()) attrs['_reverse_db_field_map'] = dict( (v, k) for k, v in attrs['_db_field_map'].iteritems()) @@ -1302,7 +1305,10 @@ class BaseDocument(object): return value def __iter__(self): - return iter(self._fields) + if 'id' in self._fields and 'id' not in self._fields_ordered: + return iter(('id', ) + self._fields_ordered) + + return iter(self._fields_ordered) def __getitem__(self, name): """Dictionary-style field access, return a field's value if present. From fc1ce6d39bd5dac86111276eae26de407a194ce6 Mon Sep 17 00:00:00 2001 From: Alice Bevan-McGregor Date: Wed, 3 Apr 2013 15:00:51 -0400 Subject: [PATCH 18/37] Allow construction of document instances using positional arguments. --- mongoengine/base.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/mongoengine/base.py b/mongoengine/base.py index 2f956ccf..36d7c295 100644 --- a/mongoengine/base.py +++ b/mongoengine/base.py @@ -907,7 +907,17 @@ class BaseDocument(object): _dynamic_lock = True _initialised = False - def __init__(self, **values): + def __init__(self, *args, **values): + if args: + # Combine positional arguments with named arguments. + # We only want named arguments. + field = iter(self._fields_ordered) + for value in args: + name = next(field) + if name in values: + raise TypeError("Multiple values for keyword argument '" + name + "'") + values[name] = value + signals.pre_init.send(self.__class__, document=self, values=values) self._data = {} From 07d3e52e6a461eeca1251911e5440486e0832c2a Mon Sep 17 00:00:00 2001 From: Alice Bevan-McGregor Date: Wed, 3 Apr 2013 15:03:33 -0400 Subject: [PATCH 19/37] Tests for construction using positional parameters. --- tests/test_document.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/test_document.py b/tests/test_document.py index 3e8d8134..00059fa0 100644 --- a/tests/test_document.py +++ b/tests/test_document.py @@ -1386,6 +1386,28 @@ class DocumentTest(unittest.TestCase): person = self.Person(name="Test User", age=30) self.assertEqual(person.name, "Test User") self.assertEqual(person.age, 30) + + def test_positional_creation(self): + """Ensure that document may be created using positional arguments. + """ + person = self.Person("Test User", 42) + self.assertEqual(person.name, "Test User") + self.assertEqual(person.age, 42) + + def test_mixed_creation(self): + """Ensure that document may be created using mixed arguments. + """ + person = self.Person("Test User", age=42) + self.assertEqual(person.name, "Test User") + self.assertEqual(person.age, 42) + + def test_bad_mixed_creation(self): + """Ensure that document gives correct error when duplicating arguments + """ + def construct_bad_instance(): + return self.Person("Test User", 42, name="Bad User") + + self.assertRaises(TypeError, construct_bad_instance) def test_to_dbref(self): """Ensure that you can get a dbref of a document""" From 37740dc01042ec7c8ce60ee365eae591de283032 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Fri, 12 Apr 2013 14:05:08 +0000 Subject: [PATCH 20/37] Added kwargs to doc.save to help interop with django (#223, #270) --- docs/changelog.rst | 1 + mongoengine/document.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index ccf099fc..8619a635 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,6 +4,7 @@ Changelog Changes in 0.7.10 ================= +- Added kwargs to doc.save to help interop with django (#223, #270) - Fixed cloning querysets in PY3 - Int fields no longer unset in save when changed to 0 (#272) - Fixed ReferenceField query chaining bug fixed (#254) diff --git a/mongoengine/document.py b/mongoengine/document.py index 7b3afafb..a251f589 100644 --- a/mongoengine/document.py +++ b/mongoengine/document.py @@ -164,7 +164,7 @@ class Document(BaseDocument): def save(self, safe=True, force_insert=False, validate=True, write_options=None, cascade=None, cascade_kwargs=None, - _refs=None): + _refs=None, **kwargs): """Save the :class:`~mongoengine.Document` to the database. If the document already exists, it will be updated, otherwise it will be created. From 63edd16a925b775251e04bbf362d3540748e138d Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Fri, 12 Apr 2013 14:20:44 +0000 Subject: [PATCH 21/37] Resolve field name to db field name when using distinct(#260, #264, #269) --- AUTHORS | 4 +++- docs/changelog.rst | 1 + mongoengine/queryset.py | 7 +++++-- tests/test_queryset.py | 19 +++++++++++++++++++ 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/AUTHORS b/AUTHORS index fe62026d..9d31c955 100644 --- a/AUTHORS +++ b/AUTHORS @@ -128,4 +128,6 @@ that much better: * Peter Teichman * Jakub Kot * Jorge Bastida - * Aleksandr Sorokoumov \ No newline at end of file + * Aleksandr Sorokoumov + * Yohan Graterol + * bool-dev \ No newline at end of file diff --git a/docs/changelog.rst b/docs/changelog.rst index 8619a635..667413e2 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,6 +4,7 @@ Changelog Changes in 0.7.10 ================= +- Resolve field name to db field name when using distinct(#260, #264, #269) - Added kwargs to doc.save to help interop with django (#223, #270) - Fixed cloning querysets in PY3 - Int fields no longer unset in save when changed to 0 (#272) diff --git a/mongoengine/queryset.py b/mongoengine/queryset.py index 20b18b7e..4aeff833 100644 --- a/mongoengine/queryset.py +++ b/mongoengine/queryset.py @@ -1212,8 +1212,11 @@ class QuerySet(object): .. versionchanged:: 0.5 - Fixed handling references .. versionchanged:: 0.6 - Improved db_field refrence handling """ - return self._dereference(self._cursor.distinct(field), 1, - name=field, instance=self._document) + try: + field = self._fields_to_dbfields([field]).pop() + finally: + return self._dereference(self._cursor.distinct(field), 1, + name=field, instance=self._document) def only(self, *fields): """Load only a subset of this document's fields. :: diff --git a/tests/test_queryset.py b/tests/test_queryset.py index 43bb70bc..88daa5fa 100644 --- a/tests/test_queryset.py +++ b/tests/test_queryset.py @@ -2486,6 +2486,25 @@ class QuerySetTest(unittest.TestCase): self.assertEqual(Foo.objects.distinct("bar"), [bar]) + def test_distinct_handles_db_field(self): + """Ensure that distinct resolves field name to db_field as expected. + """ + class Product(Document): + product_id = IntField(db_field='pid') + + Product.drop_collection() + + Product(product_id=1).save() + Product(product_id=2).save() + Product(product_id=1).save() + + self.assertEqual(set(Product.objects.distinct('product_id')), + set([1, 2])) + self.assertEqual(set(Product.objects.distinct('pid')), + set([1, 2])) + + Product.drop_collection() + def test_custom_manager(self): """Ensure that custom QuerySetManager instances work as expected. """ From 2f19b22bb227097beabbc37ef5abcd86bb621074 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Fri, 12 Apr 2013 14:25:43 +0000 Subject: [PATCH 22/37] Added dereference support for tuples (#250) --- AUTHORS | 3 ++- docs/changelog.rst | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index 9d31c955..aeb672cb 100644 --- a/AUTHORS +++ b/AUTHORS @@ -130,4 +130,5 @@ that much better: * Jorge Bastida * Aleksandr Sorokoumov * Yohan Graterol - * bool-dev \ No newline at end of file + * bool-dev + * Russ Weeks \ No newline at end of file diff --git a/docs/changelog.rst b/docs/changelog.rst index 667413e2..5ecae628 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,6 +4,7 @@ Changelog Changes in 0.7.10 ================= +- Added dereference support for tuples (#250) - Resolve field name to db field name when using distinct(#260, #264, #269) - Added kwargs to doc.save to help interop with django (#223, #270) - Fixed cloning querysets in PY3 From c9a5710554cca690f6ee2a574d03be05d93a3e78 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Fri, 12 Apr 2013 15:56:40 +0000 Subject: [PATCH 23/37] Fixed order_by chaining issue (#265) --- docs/changelog.rst | 1 + mongoengine/queryset.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 5ecae628..65a5aaf1 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,6 +4,7 @@ Changelog Changes in 0.7.10 ================= +- Fixed order_by chaining issue (#265) - Added dereference support for tuples (#250) - Resolve field name to db field name when using distinct(#260, #264, #269) - Added kwargs to doc.save to help interop with django (#223, #270) diff --git a/mongoengine/queryset.py b/mongoengine/queryset.py index 4aeff833..727f56ea 100644 --- a/mongoengine/queryset.py +++ b/mongoengine/queryset.py @@ -1321,7 +1321,8 @@ class QuerySet(object): key_list.append((key, direction)) self._ordering = key_list - + if self._cursor_obj: + self._cursor_obj.sort(key_list) return self def explain(self, format=False): From b6977a88ea02fba91809c19276852bb691e1c6ca Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Mon, 15 Apr 2013 07:32:04 +0000 Subject: [PATCH 24/37] Explicitly check for Document instances when dereferencing (#261) --- AUTHORS | 3 ++- docs/changelog.rst | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index aeb672cb..991b10ac 100644 --- a/AUTHORS +++ b/AUTHORS @@ -131,4 +131,5 @@ that much better: * Aleksandr Sorokoumov * Yohan Graterol * bool-dev - * Russ Weeks \ No newline at end of file + * Russ Weeks + * Paul Swartz \ No newline at end of file diff --git a/docs/changelog.rst b/docs/changelog.rst index 65a5aaf1..2ff2f6b4 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,6 +4,7 @@ Changelog Changes in 0.7.10 ================= +- Explicitly check for Document instances when dereferencing (#261) - Fixed order_by chaining issue (#265) - Added dereference support for tuples (#250) - Resolve field name to db field name when using distinct(#260, #264, #269) From da7a8939dfce47d866641cdaa7ab29fc444db2c9 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Mon, 15 Apr 2013 07:41:04 +0000 Subject: [PATCH 25/37] Also check if a TopLevelMetaclass instance (#261) --- mongoengine/dereference.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mongoengine/dereference.py b/mongoengine/dereference.py index 4df1fe87..997b7858 100644 --- a/mongoengine/dereference.py +++ b/mongoengine/dereference.py @@ -33,7 +33,7 @@ class DeReference(object): self.max_depth = max_depth doc_type = None - if instance and isinstance(instance, Document): + if instance and isinstance(instance, (Document, TopLevelDocumentMetaclass)): doc_type = instance._fields.get(name) if hasattr(doc_type, 'field'): doc_type = doc_type.field From 97a98f004530c078a3a0ce5e687808b3d250f362 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Mon, 15 Apr 2013 07:52:04 +0000 Subject: [PATCH 26/37] Only mark a field as changed if the value has changed (#258) --- tests/test_dereference.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/tests/test_dereference.py b/tests/test_dereference.py index d7438d2f..09001540 100644 --- a/tests/test_dereference.py +++ b/tests/test_dereference.py @@ -185,8 +185,9 @@ class FieldTest(unittest.TestCase): # Migrate the data for g in Group.objects(): - g.author = g.author - g.members = g.members + # Explicitly mark as changed so resets + g._mark_as_changed('author') + g._mark_as_changed('members') g.save() group = Group.objects.first() @@ -997,7 +998,7 @@ class FieldTest(unittest.TestCase): msg = Message.objects.get(id=1) self.assertEqual(0, msg.comments[0].id) self.assertEqual(1, msg.comments[1].id) - + def test_tuples_as_tuples(self): """ Ensure that tuples remain tuples when they are @@ -1007,16 +1008,16 @@ class FieldTest(unittest.TestCase): class EnumField(BaseField): def __init__(self, **kwargs): super(EnumField,self).__init__(**kwargs) - + def to_mongo(self, value): return value - + def to_python(self, value): return tuple(value) - + class TestDoc(Document): items = ListField(EnumField()) - + TestDoc.drop_collection() tuples = [(100,'Testing')] doc = TestDoc() From 757ff31661282d0296e1927b694a7951bfee6e9a Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Mon, 15 Apr 2013 07:53:57 +0000 Subject: [PATCH 27/37] Updated changelog --- docs/changelog.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 2ff2f6b4..45b2e09a 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,6 +4,7 @@ Changelog Changes in 0.7.10 ================= +- Only mark a field as changed if the value has changed (#258) - Explicitly check for Document instances when dereferencing (#261) - Fixed order_by chaining issue (#265) - Added dereference support for tuples (#250) From b451cc567d1d3d461a1d15dace37fb79f88a5279 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Mon, 15 Apr 2013 07:59:24 +0000 Subject: [PATCH 28/37] Return '_id' as the key for document.id in _data dictionary * Re #146 Conflicts: mongoengine/base.py --- mongoengine/base.py | 6 +++--- tests/test_document.py | 20 ++++++++++++++++++-- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/mongoengine/base.py b/mongoengine/base.py index 25c3bbd5..7e6d0aa7 100644 --- a/mongoengine/base.py +++ b/mongoengine/base.py @@ -192,7 +192,7 @@ class BaseField(object): return self # Get value from document instance if available, if not use default - value = instance._data.get(self.name) + value = instance._data.get(self.name or self.db_field) if value is None: value = self.default @@ -207,9 +207,9 @@ class BaseField(object): """ changed = False if (self.name not in instance._data or - instance._data[self.name] != value): + instance._data[self.name or self.db_field] != value): changed = True - instance._data[self.name] = value + instance._data[self.name or self.db_field] = value if changed and instance._initialised: instance._mark_as_changed(self.name) diff --git a/tests/test_document.py b/tests/test_document.py index 3e8d8134..b5542c25 100644 --- a/tests/test_document.py +++ b/tests/test_document.py @@ -1529,8 +1529,10 @@ class DocumentTest(unittest.TestCase): doc.validate() keys = doc._data.keys() self.assertEqual(2, len(keys)) - self.assertTrue(None in keys) self.assertTrue('e' in keys) + # Ensure that the _id field has the right id + self.assertTrue('_id' in keys) + self.assertEqual(doc._data.get('_id'), doc.id) def test_save(self): """Ensure that a document may be saved in the database. @@ -3368,6 +3370,21 @@ class DocumentTest(unittest.TestCase): } ) ]), "1,2") + def test_data_contains_idfield(self): + """Ensure that asking for _data returns 'id' + """ + class Person(Document): + name = StringField() + + Person.drop_collection() + person = Person() + person.name = "Harry Potter" + person.save(cascade=False) + + person = Person.objects.first() + self.assertTrue('_id' in person._data.keys()) + self.assertEqual(person._data.get('_id'), person.id) + class ValidatorErrorTest(unittest.TestCase): @@ -3521,6 +3538,5 @@ class ValidatorErrorTest(unittest.TestCase): self.assertRaises(OperationError, change_shard_key) - if __name__ == '__main__': unittest.main() From 6186691259898c8e5d9da8983a74792d440622cb Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Mon, 15 Apr 2013 08:01:24 +0000 Subject: [PATCH 29/37] Updated changelog and AUTHORS (#255) --- AUTHORS | 3 ++- docs/changelog.rst | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index 991b10ac..aa223ed1 100644 --- a/AUTHORS +++ b/AUTHORS @@ -132,4 +132,5 @@ that much better: * Yohan Graterol * bool-dev * Russ Weeks - * Paul Swartz \ No newline at end of file + * Paul Swartz + * Sundar Raman \ No newline at end of file diff --git a/docs/changelog.rst b/docs/changelog.rst index 45b2e09a..90d4d66a 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,6 +4,7 @@ Changelog Changes in 0.7.10 ================= +- Added "_id" to _data dictionary (#255) - Only mark a field as changed if the value has changed (#258) - Explicitly check for Document instances when dereferencing (#261) - Fixed order_by chaining issue (#265) From d80b1a774934ae1d79e8b43ded5fbff8e86c9d8a Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Mon, 15 Apr 2013 08:03:51 +0000 Subject: [PATCH 30/37] Test clean up (#255) --- tests/test_document.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test_document.py b/tests/test_document.py index b5542c25..051dc2a3 100644 --- a/tests/test_document.py +++ b/tests/test_document.py @@ -3377,9 +3377,7 @@ class DocumentTest(unittest.TestCase): name = StringField() Person.drop_collection() - person = Person() - person.name = "Harry Potter" - person.save(cascade=False) + Person(name="Harry Potter").save() person = Person.objects.first() self.assertTrue('_id' in person._data.keys()) From a5257643596836f2f58fccce55157e83dc4b4f27 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Tue, 16 Apr 2013 20:12:01 +0000 Subject: [PATCH 31/37] Fixed clearing _changed_fields for complex nested embedded documents (#237, #239, #242) --- AUTHORS | 3 ++- docs/changelog.rst | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index aa223ed1..1c72b31a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -133,4 +133,5 @@ that much better: * bool-dev * Russ Weeks * Paul Swartz - * Sundar Raman \ No newline at end of file + * Sundar Raman + * Benoit Louy \ No newline at end of file diff --git a/docs/changelog.rst b/docs/changelog.rst index 90d4d66a..99aa49aa 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,6 +4,7 @@ Changelog Changes in 0.7.10 ================= +- Fixed clearing _changed_fields for complex nested embedded documents (#237, #239, #242) - Added "_id" to _data dictionary (#255) - Only mark a field as changed if the value has changed (#258) - Explicitly check for Document instances when dereferencing (#261) From 6fe074fb13606ad82bb392f523acf412f65a66df Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Tue, 16 Apr 2013 20:21:11 +0000 Subject: [PATCH 32/37] Fixed issue with numerical keys in MapField(EmbeddedDocumentField()) (#240) --- AUTHORS | 3 ++- docs/changelog.rst | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index 1c72b31a..01f6f22e 100644 --- a/AUTHORS +++ b/AUTHORS @@ -134,4 +134,5 @@ that much better: * Russ Weeks * Paul Swartz * Sundar Raman - * Benoit Louy \ No newline at end of file + * Benoit Louy + * lraucy \ No newline at end of file diff --git a/docs/changelog.rst b/docs/changelog.rst index 99aa49aa..269a4228 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,6 +4,7 @@ Changelog Changes in 0.7.10 ================= +- Fixed issue with numerical keys in MapField(EmbeddedDocumentField()) (#240) - Fixed clearing _changed_fields for complex nested embedded documents (#237, #239, #242) - Added "_id" to _data dictionary (#255) - Only mark a field as changed if the value has changed (#258) From d02de0798f8ee0b04bbb33e2ca0c71651f32af09 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Tue, 16 Apr 2013 20:26:23 +0000 Subject: [PATCH 33/37] Documentation fix explaining adding a dummy backend for django (#172) --- docs/django.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/django.rst b/docs/django.rst index 144baab5..58597469 100644 --- a/docs/django.rst +++ b/docs/django.rst @@ -10,6 +10,16 @@ In your **settings.py** file, ignore the standard database settings (unless you also plan to use the ORM in your project), and instead call :func:`~mongoengine.connect` somewhere in the settings module. +.. note :: + If you are not using another Database backend make sure you add a dummy + backend, by adding the following to ``settings.py``:: + + DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.dummy' + } + } + Authentication ============== MongoEngine includes a Django authentication backend, which uses MongoDB. The From 1f9ec0c888f6a5ea5a6e83267d361b917e7df104 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Tue, 16 Apr 2013 20:30:40 +0000 Subject: [PATCH 34/37] Added Django sessions TTL support (#224) --- AUTHORS | 3 ++- docs/changelog.rst | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index 01f6f22e..a689ec6e 100644 --- a/AUTHORS +++ b/AUTHORS @@ -135,4 +135,5 @@ that much better: * Paul Swartz * Sundar Raman * Benoit Louy - * lraucy \ No newline at end of file + * lraucy + * hellysmile \ No newline at end of file diff --git a/docs/changelog.rst b/docs/changelog.rst index 269a4228..df15855c 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,6 +4,7 @@ Changelog Changes in 0.7.10 ================= +- Added Django sessions TTL support (#224) - Fixed issue with numerical keys in MapField(EmbeddedDocumentField()) (#240) - Fixed clearing _changed_fields for complex nested embedded documents (#237, #239, #242) - Added "_id" to _data dictionary (#255) From 3a85422e8f332484984a0b519f8cd614b7445148 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Tue, 16 Apr 2013 20:35:29 +0000 Subject: [PATCH 35/37] Added 64-bit integer support (#251) --- AUTHORS | 3 ++- docs/changelog.rst | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index a689ec6e..370f082c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -136,4 +136,5 @@ that much better: * Sundar Raman * Benoit Louy * lraucy - * hellysmile \ No newline at end of file + * hellysmile + * Jaepil Jeong \ No newline at end of file diff --git a/docs/changelog.rst b/docs/changelog.rst index df15855c..8ff95b56 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,6 +4,7 @@ Changelog Changes in 0.7.10 ================= +- Added 64-bit integer support (#251) - Added Django sessions TTL support (#224) - Fixed issue with numerical keys in MapField(EmbeddedDocumentField()) (#240) - Fixed clearing _changed_fields for complex nested embedded documents (#237, #239, #242) From b562e209d17afda19413c2971e69e2633f1f09e5 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Tue, 16 Apr 2013 20:46:02 +0000 Subject: [PATCH 36/37] Updated EmailField length to support long domains (#243) --- docs/changelog.rst | 1 + mongoengine/fields.py | 2 +- tests/test_fields.py | 18 ++++++++++++++++-- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 8ff95b56..91d4da04 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,6 +4,7 @@ Changelog Changes in 0.7.10 ================= +- Updated EmailField length to support long domains (#243) - Added 64-bit integer support (#251) - Added Django sessions TTL support (#224) - Fixed issue with numerical keys in MapField(EmbeddedDocumentField()) (#240) diff --git a/mongoengine/fields.py b/mongoengine/fields.py index c40491b6..2d1ee71c 100644 --- a/mongoengine/fields.py +++ b/mongoengine/fields.py @@ -143,7 +143,7 @@ class EmailField(StringField): EMAIL_REGEX = re.compile( r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*" # dot-atom r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-011\013\014\016-\177])*"' # quoted-string - r')@(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?$', re.IGNORECASE # domain + r')@(?:[A-Z0-9](?:[A-Z0-9-]{0,253}[A-Z0-9])?\.)+[A-Z]{2,6}\.?$', re.IGNORECASE # domain ) def validate(self, value): diff --git a/tests/test_fields.py b/tests/test_fields.py index 1e693e8b..55ac6fb7 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -2371,12 +2371,26 @@ class FieldTest(unittest.TestCase): self.assertTrue(1 in error_dict['comments']) self.assertTrue('content' in error_dict['comments'][1]) self.assertEqual(error_dict['comments'][1]['content'], - u'Field is required') - + u'Field is required') post.comments[1].content = 'here we go' post.validate() + def test_email_field(self): + class User(Document): + email = EmailField() + + user = User(email="ross@example.com") + self.assertTrue(user.validate() is None) + + user = User(email=("Kofq@rhom0e4klgauOhpbpNdogawnyIKvQS0wk2mjqrgGQ5S" + "ucictfqpdkK9iS1zeFw8sg7s7cwAF7suIfUfeyueLpfosjn3" + "aJIazqqWkm7.net")) + self.assertTrue(user.validate() is None) + + user = User(email='me@localhost') + self.assertRaises(ValidationError, user.validate) + def test_email_field_honors_regex(self): class User(Document): email = EmailField(regex=r'\w+@example.com') From b4d87d91282247be21d46107935f31891a1802d6 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Tue, 16 Apr 2013 20:50:34 +0000 Subject: [PATCH 37/37] Updated changelog --- docs/changelog.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 91d4da04..0443f74e 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,6 +4,7 @@ Changelog Changes in 0.7.10 ================= +- Allow construction using positional parameters (#268) - Updated EmailField length to support long domains (#243) - Added 64-bit integer support (#251) - Added Django sessions TTL support (#224)