From 787fc1cd8ba115aa1186952833a29e8ee3b0d45e Mon Sep 17 00:00:00 2001 From: yak Date: Tue, 13 Nov 2012 13:02:07 +0100 Subject: [PATCH 01/25] bug fix for RefferenceField.to_mongo when dbref=False --- mongoengine/fields.py | 2 +- tests/test_fields.py | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/mongoengine/fields.py b/mongoengine/fields.py index 01d3fc63..ee029065 100644 --- a/mongoengine/fields.py +++ b/mongoengine/fields.py @@ -777,7 +777,7 @@ class ReferenceField(BaseField): def to_mongo(self, document): if isinstance(document, DBRef): if not self.dbref: - return DBRef.id + return document.id return document elif not self.dbref and isinstance(document, basestring): return document diff --git a/tests/test_fields.py b/tests/test_fields.py index 98065501..abc50a3f 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1104,6 +1104,15 @@ class FieldTest(unittest.TestCase): p = Person.objects.get(name="Ross") self.assertEqual(p.parent, p1) + + def test_dbref_to_mongo(self): + class Person(Document): + name = StringField() + parent = ReferenceField('self', dbref=False) + + p1 = Person._from_son({'name':"Yakxxx", 'parent': "50a234ea469ac1eda42d347d"}) + mongoed = p1.to_mongo() + self.assertIsInstance(mongoed['parent'], ObjectId) def test_objectid_reference_fields(self): From 0da2dfd191143dcf11d3281cea7b3da2c790c99d Mon Sep 17 00:00:00 2001 From: yak Date: Tue, 13 Nov 2012 13:04:05 +0100 Subject: [PATCH 02/25] addition to AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 6ba2f88d..bae1dcba 100644 --- a/AUTHORS +++ b/AUTHORS @@ -124,3 +124,4 @@ that much better: * Stefan Wójcik * dimonb * Garry Polley + * Jakub Kot From 2c0fc142a385f791e6cc67b5bf624eb6e2df82ed Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Mon, 26 Nov 2012 21:04:06 +0000 Subject: [PATCH 03/25] Updated travis.yml --- .travis.yml | 2 +- tests/test_dereference.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c10a1f36..1aa97746 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,4 +26,4 @@ notifications: branches: only: - master - - 0.7 \ No newline at end of file + - 0.8 \ No newline at end of file diff --git a/tests/test_dereference.py b/tests/test_dereference.py index 7b149dbd..0eb891cb 100644 --- a/tests/test_dereference.py +++ b/tests/test_dereference.py @@ -42,6 +42,12 @@ class FieldTest(unittest.TestCase): group_obj = Group.objects.first() self.assertEqual(q, 1) + len(group_obj._data['members']) + self.assertEqual(q, 1) + + len(group_obj.members) + self.assertEqual(q, 2) + [m for m in group_obj.members] self.assertEqual(q, 2) From 66c6d14f7ac5838f48664b48a1903cb8e72b558f Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Tue, 27 Nov 2012 10:50:22 +0000 Subject: [PATCH 04/25] Trying to fix seesaw test on travis --- tests/test_all_warnings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_all_warnings.py b/tests/test_all_warnings.py index 9b38fa61..8297e453 100644 --- a/tests/test_all_warnings.py +++ b/tests/test_all_warnings.py @@ -53,7 +53,7 @@ class TestWarnings(unittest.TestCase): p2.parent = p1 p2.save(cascade=False) - self.assertEqual(len(self.warning_list), 1) + self.assertTrue(len(self.warning_list) > 0) warning = self.warning_list[0] self.assertEqual(FutureWarning, warning["category"]) self.assertTrue("ReferenceFields will default to using ObjectId" From 9f5ab8149f4085a305f9a7b86ce3d4e9ab497514 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Tue, 27 Nov 2012 11:06:55 +0000 Subject: [PATCH 05/25] Adding some debugging --- tests/test_all_warnings.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_all_warnings.py b/tests/test_all_warnings.py index 8297e453..7ef1f210 100644 --- a/tests/test_all_warnings.py +++ b/tests/test_all_warnings.py @@ -77,6 +77,8 @@ class TestWarnings(unittest.TestCase): p2.save() self.assertEqual(len(self.warning_list), 1) + if len(self.warning_list) > 1: + print self.warning_list warning = self.warning_list[0] self.assertEqual(FutureWarning, warning["category"]) self.assertTrue("Cascading saves will default to off in 0.8" From 653c4259eebc0f37e1e2b82d5510d5c4c9475f87 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Tue, 27 Nov 2012 11:59:34 +0000 Subject: [PATCH 06/25] Fixed handling for old style types --- docs/changelog.rst | 4 ++++ mongoengine/__init__.py | 2 +- mongoengine/base.py | 9 ++++----- python-mongoengine.spec | 2 +- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index aac24c62..7457eeb4 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -2,6 +2,10 @@ Changelog ========= +Changes in 0.7.7 +================ +- Fix handling for old style _types + Changes in 0.7.6 ================ - Unicode fix for repr (MongoEngine/mongoengine#133) diff --git a/mongoengine/__init__.py b/mongoengine/__init__.py index cdfbfff9..9f1f552f 100644 --- a/mongoengine/__init__.py +++ b/mongoengine/__init__.py @@ -12,7 +12,7 @@ from signals import * __all__ = (document.__all__ + fields.__all__ + connection.__all__ + queryset.__all__ + signals.__all__) -VERSION = (0, 7, 6) +VERSION = (0, 7, 7) def get_version(): diff --git a/mongoengine/base.py b/mongoengine/base.py index fa12e35d..208e0e59 100644 --- a/mongoengine/base.py +++ b/mongoengine/base.py @@ -121,11 +121,10 @@ class ValidationError(AssertionError): def get_document(name): doc = _document_registry.get(name, None) if not doc: - # Possible old style names - end = ".%s" % name - possible_match = [k for k in _document_registry.keys() - if k.endswith(end)] - if len(possible_match) == 1: + # Possible old style name + end = name.split('.')[-1] + possible_match = [k for k in _document_registry.keys() if k == end] + if len(possible_match) == 1 and end != name: doc = _document_registry.get(possible_match.pop(), None) if not doc: raise NotRegistered(""" diff --git a/python-mongoengine.spec b/python-mongoengine.spec index d796f993..9a376ec7 100644 --- a/python-mongoengine.spec +++ b/python-mongoengine.spec @@ -5,7 +5,7 @@ %define srcname mongoengine Name: python-%{srcname} -Version: 0.7.6 +Version: 0.7.7 Release: 1%{?dist} Summary: A Python Document-Object Mapper for working with MongoDB From 9d52e1865931025ec4c10212db70b0b727e2a91a Mon Sep 17 00:00:00 2001 From: Peter Teichman Date: Wed, 21 Nov 2012 13:22:10 -0500 Subject: [PATCH 07/25] Don't freeze the current query state when calling .order_by() This changes order_by() to eliminate its reference to self._cursor. This meant that any parameters built by QuerySet that followed an order_by() clause were ignored. --- mongoengine/queryset.py | 6 ++++-- tests/test_queryset.py | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/mongoengine/queryset.py b/mongoengine/queryset.py index 5c7b9c88..58b19596 100644 --- a/mongoengine/queryset.py +++ b/mongoengine/queryset.py @@ -586,11 +586,13 @@ class QuerySet(object): if self._where_clause: self._cursor_obj.where(self._where_clause) - # apply default ordering if self._ordering: + # Apply query ordering self._cursor_obj.sort(self._ordering) elif self._document._meta['ordering']: + # Otherwise, apply the ordering from the document model self.order_by(*self._document._meta['ordering']) + self._cursor_obj.sort(self._ordering) if self._limit is not None: self._cursor_obj.limit(self._limit - (self._skip or 0)) @@ -1274,7 +1276,7 @@ class QuerySet(object): key_list.append((key, direction)) self._ordering = key_list - self._cursor.sort(key_list) + return self def explain(self, format=False): diff --git a/tests/test_queryset.py b/tests/test_queryset.py index 8f846ea6..48d2a263 100644 --- a/tests/test_queryset.py +++ b/tests/test_queryset.py @@ -1793,6 +1793,22 @@ 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_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 C", age=30).save() + + only_age = self.Person.objects.order_by('-age').only('age') + + names = [p.name for p in only_age] + ages = [p.age for p in only_age] + + # The .only('age') clause should mean that all names are None + self.assertEqual(names, [None, None, None]) + 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 3bdc9a2f0940cf03d6f72212e4db3148f0a0cfb7 Mon Sep 17 00:00:00 2001 From: Adrian Scott Date: Thu, 29 Nov 2012 20:53:09 -0500 Subject: [PATCH 08/25] session collection parameter; encoding optional Added a parameter for the name of the session collection; Added the option to not encode session_data, which is useful for expiring sessions of users when a password is changed, etc.; these upgrades provided by SocialVilla Inc. --- mongoengine/django/sessions.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/mongoengine/django/sessions.py b/mongoengine/django/sessions.py index f1783429..0330ee1b 100644 --- a/mongoengine/django/sessions.py +++ b/mongoengine/django/sessions.py @@ -15,13 +15,20 @@ MONGOENGINE_SESSION_DB_ALIAS = getattr( settings, 'MONGOENGINE_SESSION_DB_ALIAS', DEFAULT_CONNECTION_NAME) +MONGOENGINE_SESSION_COLLECTION = getattr( + settings, 'MONGOENGINE_SESSION_COLLECTION', + 'django_session') + +MONGOENGINE_SESSION_DATA_ENCODE = getattr( + settings, 'MONGOENGINE_SESSION_DATA_ENCODE', + True) class MongoSession(Document): session_key = fields.StringField(primary_key=True, max_length=40) - session_data = fields.StringField() + session_data = fields.StringField() if MONGOENGINE_SESSION_DATA_ENCODE else fields.DictField() expire_date = fields.DateTimeField() - meta = {'collection': 'django_session', + meta = {'collection': MONGOENGINE_SESSION_COLLECTION, 'db_alias': MONGOENGINE_SESSION_DB_ALIAS, 'allow_inheritance': False} @@ -34,7 +41,10 @@ class SessionStore(SessionBase): try: s = MongoSession.objects(session_key=self.session_key, expire_date__gt=datetime.now())[0] - return self.decode(force_unicode(s.session_data)) + if MONGOENGINE_SESSION_DATA_ENCODE: + return self.decode(force_unicode(s.session_data)) + else: + return s.session_data except (IndexError, SuspiciousOperation): self.create() return {} @@ -57,7 +67,10 @@ class SessionStore(SessionBase): if self.session_key is None: self._session_key = self._get_new_session_key() s = MongoSession(session_key=self.session_key) - s.session_data = self.encode(self._get_session(no_load=must_create)) + if MONGOENGINE_SESSION_DATA_ENCODE: + s.session_data = self.encode(self._get_session(no_load=must_create)) + else: + s.session_data = self._get_session(no_load=must_create) s.expire_date = self.get_expiry_date() try: s.save(force_insert=must_create, safe=True) From b10d76cf4b396edf5473a386717a31831b749500 Mon Sep 17 00:00:00 2001 From: Adrian Scott Date: Thu, 29 Nov 2012 21:28:03 -0500 Subject: [PATCH 09/25] split line to meet 79 char max line limit --- mongoengine/django/sessions.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mongoengine/django/sessions.py b/mongoengine/django/sessions.py index 0330ee1b..ca7b01fc 100644 --- a/mongoengine/django/sessions.py +++ b/mongoengine/django/sessions.py @@ -25,7 +25,8 @@ MONGOENGINE_SESSION_DATA_ENCODE = getattr( class MongoSession(Document): session_key = fields.StringField(primary_key=True, max_length=40) - session_data = fields.StringField() if MONGOENGINE_SESSION_DATA_ENCODE else fields.DictField() + session_data = fields.StringField() if MONGOENGINE_SESSION_DATA_ENCODE \ + else fields.DictField() expire_date = fields.DateTimeField() meta = {'collection': MONGOENGINE_SESSION_COLLECTION, From 4fe87b40da989bdd8caecc56fe48e025f0061ade Mon Sep 17 00:00:00 2001 From: Adrian Scott Date: Thu, 29 Nov 2012 21:49:54 -0500 Subject: [PATCH 10/25] added comments --- mongoengine/django/sessions.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mongoengine/django/sessions.py b/mongoengine/django/sessions.py index ca7b01fc..810b6265 100644 --- a/mongoengine/django/sessions.py +++ b/mongoengine/django/sessions.py @@ -15,10 +15,12 @@ MONGOENGINE_SESSION_DB_ALIAS = getattr( settings, 'MONGOENGINE_SESSION_DB_ALIAS', DEFAULT_CONNECTION_NAME) +# a setting for the name of the collection used to store sessions MONGOENGINE_SESSION_COLLECTION = getattr( settings, 'MONGOENGINE_SESSION_COLLECTION', 'django_session') +# a setting for whether session data is stored encoded or not MONGOENGINE_SESSION_DATA_ENCODE = getattr( settings, 'MONGOENGINE_SESSION_DATA_ENCODE', True) From 376d1c97ab9bce3df6f060bc18e9edd6c64a3e87 Mon Sep 17 00:00:00 2001 From: Shaun Duncan Date: Tue, 4 Dec 2012 13:08:49 -0500 Subject: [PATCH 11/25] EmailField should honor StringField validation as well --- mongoengine/fields.py | 1 + tests/test_fields.py | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/mongoengine/fields.py b/mongoengine/fields.py index 1f865605..368c0009 100644 --- a/mongoengine/fields.py +++ b/mongoengine/fields.py @@ -141,6 +141,7 @@ class EmailField(StringField): def validate(self, value): if not EmailField.EMAIL_REGEX.match(value): self.error('Invalid Mail-address: %s' % value) + super(EmailField, self).validate(value) class IntField(BaseField): diff --git a/tests/test_fields.py b/tests/test_fields.py index 68c79b5c..db23d936 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -2114,6 +2114,18 @@ class FieldTest(unittest.TestCase): post.comments[1].content = 'here we go' post.validate() + def test_email_field_honors_regex(self): + class User(Document): + email = EmailField(regex=r'\w+@example.com') + + # Fails regex validation + user = User(email='me@foo.com') + self.assertRaises(ValidationError, user.validate) + + # Passes regex validation + user = User(email='me@example.com') + self.assertTrue(user.validate() is None) + if __name__ == '__main__': unittest.main() From 94adc207ad86883eb22d592a5844fb82e62c4b6f Mon Sep 17 00:00:00 2001 From: Jorge Bastida Date: Fri, 7 Dec 2012 11:20:27 +0000 Subject: [PATCH 12/25] First as_pymongo implementation --- mongoengine/queryset.py | 12 ++++++++++++ tests/test_queryset.py | 16 ++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/mongoengine/queryset.py b/mongoengine/queryset.py index c774322e..4a27caff 100644 --- a/mongoengine/queryset.py +++ b/mongoengine/queryset.py @@ -353,6 +353,7 @@ class QuerySet(object): self._slave_okay = False self._iter = False self._scalar = [] + self._as_pymongo = False # If inheritance is allowed, only return instances and instances of # subclasses of the class being used @@ -1002,6 +1003,10 @@ class QuerySet(object): if self._scalar: return self._get_scalar(self._document._from_son( self._cursor.next())) + + if self._as_pymongo: + return self._cursor.next() + return self._document._from_son(self._cursor.next()) except StopIteration, e: self.rewind() @@ -1602,6 +1607,13 @@ class QuerySet(object): """An alias for scalar""" return self.scalar(*fields) + def as_pymongo(self): + """Instead of returning Document instances, return raw values from + pymongo. + """ + self._as_pymongo = True + return self + def _sub_js_fields(self, code): """When fields are specified with [~fieldname] syntax, where *fieldname* is the Python name of a field, *fieldname* will be diff --git a/tests/test_queryset.py b/tests/test_queryset.py index 690df5eb..1920d2f2 100644 --- a/tests/test_queryset.py +++ b/tests/test_queryset.py @@ -3691,6 +3691,22 @@ class QueryFieldListTest(unittest.TestCase): ak = list(Bar.objects(foo__match={'shape': "square", "color": "purple"})) self.assertEqual([b1], ak) + def test_as_pymongo(self): + + class User(Document): + id = ObjectIdField('_id') + name = StringField() + age = IntField() + + User.drop_collection() + User(name="Bob Dole", age=89).save() + User(name="Barack Obama", age=51).save() + + users = [u for u in User.objects.only('name').as_pymongo()] + self.assertTrue(isinstance(users[0], dict)) + self.assertTrue(isinstance(users[1], dict)) + self.assertEqual(users[0]['name'], 'Bob Dole') + self.assertEqual(users[1]['name'], 'Barack Obama') if __name__ == '__main__': unittest.main() From bb15bf8d1349fdcef597f35517ed92939eb559e2 Mon Sep 17 00:00:00 2001 From: Adrian Scott Date: Fri, 7 Dec 2012 10:02:12 -0500 Subject: [PATCH 13/25] Update AUTHORS added me (Adrian Scott, issues 180, 181) --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 6ba2f88d..80ea6131 100644 --- a/AUTHORS +++ b/AUTHORS @@ -124,3 +124,4 @@ that much better: * Stefan Wójcik * dimonb * Garry Polley + * Adrian Scott From ad983dc279c25b0305dcd32f07cb380a87609f3e Mon Sep 17 00:00:00 2001 From: Jorge Bastida Date: Fri, 7 Dec 2012 15:42:10 +0000 Subject: [PATCH 14/25] Implement _get_as_pymongo --- mongoengine/queryset.py | 51 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/mongoengine/queryset.py b/mongoengine/queryset.py index 4a27caff..be605713 100644 --- a/mongoengine/queryset.py +++ b/mongoengine/queryset.py @@ -354,6 +354,7 @@ class QuerySet(object): self._iter = False self._scalar = [] self._as_pymongo = False + self._as_pymongo_coerce = False # If inheritance is allowed, only return instances and instances of # subclasses of the class being used @@ -1003,9 +1004,8 @@ class QuerySet(object): if self._scalar: return self._get_scalar(self._document._from_son( self._cursor.next())) - if self._as_pymongo: - return self._cursor.next() + return self._get_as_pymongo(self._cursor.next()) return self._document._from_son(self._cursor.next()) except StopIteration, e: @@ -1585,6 +1585,48 @@ class QuerySet(object): return tuple(data) + def _get_as_pymongo(self, row): + # Extract which fields paths we should follow if .fields(...) was + # used. If not, handle all fields. + if not getattr(self, '__as_pymongo_fields', None): + self.__as_pymongo_fields = [] + for field in self._loaded_fields.fields - set(['_cls', '_id', '_types']): + self.__as_pymongo_fields.append(field) + while '.' in field: + field, _ = field.rsplit('.', 1) + self.__as_pymongo_fields.append(field) + + all_fields = not self.__as_pymongo_fields + + def clean(data, path=None): + path = path or '' + + if isinstance(data, dict): + new_data = {} + for key, value in data.iteritems(): + new_path = '%s.%s' % (path, key) if path else key + if all_fields or new_path in self.__as_pymongo_fields: + new_data[key] = clean(value, path=new_path) + data = new_data + elif isinstance(data, list): + data = [clean(d, path=path) for d in data] + else: + if self._as_pymongo_coerce: + # If we need to coerce types, we need to determine the + # type of this field and use the corresponding .to_python(...) + from mongoengine.fields import EmbeddedDocumentField + obj = self._document + for chunk in path.split('.'): + obj = getattr(obj, chunk, None) + if obj is None: + break + elif isinstance(obj, EmbeddedDocumentField): + obj = obj.document_type + if obj and data is not None: + data = obj.to_python(data) + return data + return clean(row) + def scalar(self, *fields): """Instead of returning Document instances, return either a specific value or a tuple of values in order. @@ -1607,11 +1649,14 @@ class QuerySet(object): """An alias for scalar""" return self.scalar(*fields) - def as_pymongo(self): + def as_pymongo(self, coerce_types=False): """Instead of returning Document instances, return raw values from pymongo. + + :param coerce_type: Field types (if applicable) would be use to coerce types. """ self._as_pymongo = True + self._as_pymongo_coerce = coerce_types return self def _sub_js_fields(self, code): From d5ec3c6a31f0409731f11a3e2fefc4b9b7184a4c Mon Sep 17 00:00:00 2001 From: Jorge Bastida Date: Fri, 7 Dec 2012 15:59:09 +0000 Subject: [PATCH 15/25] Add as_pymongo to __getitem__ and in_bulk --- mongoengine/queryset.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mongoengine/queryset.py b/mongoengine/queryset.py index be605713..d12f3bb4 100644 --- a/mongoengine/queryset.py +++ b/mongoengine/queryset.py @@ -988,6 +988,9 @@ class QuerySet(object): for doc in docs: doc_map[doc['_id']] = self._get_scalar( self._document._from_son(doc)) + elif self._as_pymongo: + for doc in docs: + doc_map[doc['_id']] = self._get_as_pymongo(doc) else: for doc in docs: doc_map[doc['_id']] = self._document._from_son(doc) @@ -1189,6 +1192,8 @@ class QuerySet(object): if self._scalar: return self._get_scalar(self._document._from_son( self._cursor[key])) + if self._as_pymongo: + return self._get_as_pymongo(self._cursor.next()) return self._document._from_son(self._cursor[key]) raise AttributeError From e62c35b040b9bcc3b9d99e6ba87b1fa34b5a5af1 Mon Sep 17 00:00:00 2001 From: Jorge Bastida Date: Fri, 7 Dec 2012 16:21:31 +0000 Subject: [PATCH 16/25] Add more tests --- tests/test_queryset.py | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/tests/test_queryset.py b/tests/test_queryset.py index 1920d2f2..09a48233 100644 --- a/tests/test_queryset.py +++ b/tests/test_queryset.py @@ -3693,20 +3693,36 @@ class QueryFieldListTest(unittest.TestCase): def test_as_pymongo(self): + from decimal import Decimal + class User(Document): id = ObjectIdField('_id') name = StringField() age = IntField() + price = DecimalField() User.drop_collection() - User(name="Bob Dole", age=89).save() - User(name="Barack Obama", age=51).save() + User(name="Bob Dole", age=89, price=Decimal('1.11')).save() + User(name="Barack Obama", age=51, price=Decimal('2.22')).save() - users = [u for u in User.objects.only('name').as_pymongo()] - self.assertTrue(isinstance(users[0], dict)) - self.assertTrue(isinstance(users[1], dict)) - self.assertEqual(users[0]['name'], 'Bob Dole') - self.assertEqual(users[1]['name'], 'Barack Obama') + users = User.objects.only('name', 'price').as_pymongo() + results = list(users) + self.assertTrue(isinstance(results[0], dict)) + self.assertTrue(isinstance(results[1], dict)) + self.assertEqual(results[0]['name'], 'Bob Dole') + self.assertEqual(results[0]['price'], '1.11') + self.assertEqual(results[1]['name'], 'Barack Obama') + self.assertEqual(results[1]['price'], '2.22') + + # Test coerce_types + users = User.objects.only('name', 'price').as_pymongo(coerce_types=True) + results = list(users) + self.assertTrue(isinstance(results[0], dict)) + self.assertTrue(isinstance(results[1], dict)) + self.assertEqual(results[0]['name'], 'Bob Dole') + self.assertEqual(results[0]['price'], Decimal('1.11')) + self.assertEqual(results[1]['name'], 'Barack Obama') + self.assertEqual(results[1]['price'], Decimal('2.22')) if __name__ == '__main__': unittest.main() From 452cd125faf161feff10f79fe080405074d21650 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Mon, 10 Dec 2012 08:11:35 +0000 Subject: [PATCH 17/25] Updated Changelog --- docs/changelog.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 7457eeb4..355449f2 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -2,6 +2,11 @@ Changelog ========= + +Changes in 0.7.8 +================ +- Added as_pymongo method to return raw or cast results from pymongo (MongoEngine/mongoengine#193) + Changes in 0.7.7 ================ - Fix handling for old style _types From 6997e0247665605ba872f82254db11328739829d Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Mon, 10 Dec 2012 08:23:41 +0000 Subject: [PATCH 18/25] Fixed EmailField so can add extra validation (MongoEngine/mongoengine#173, MongoEngine/mongoengine#174, MongoEngine/mongoengine#187) --- AUTHORS | 2 +- docs/changelog.rst | 2 ++ mongoengine/queryset.py | 2 +- tests/test_queryset.py | 4 ++++ 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index 6ba2f88d..5e6f9e7f 100644 --- a/AUTHORS +++ b/AUTHORS @@ -106,7 +106,7 @@ that much better: * Adam Reeve * Anthony Nemitz * deignacio - * shaunduncan + * Shaun Duncan * Meir Kriheli * Andrey Fedoseev * aparajita diff --git a/docs/changelog.rst b/docs/changelog.rst index 355449f2..3dff3bd5 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -5,6 +5,8 @@ Changelog Changes in 0.7.8 ================ +- Fixed EmailField so can add extra validation (MongoEngine/mongoengine#173, MongoEngine/mongoengine#174, MongoEngine/mongoengine#187) +- Fixed bulk inserts can now handle custom pk's (MongoEngine/mongoengine#192) - Added as_pymongo method to return raw or cast results from pymongo (MongoEngine/mongoengine#193) Changes in 0.7.7 diff --git a/mongoengine/queryset.py b/mongoengine/queryset.py index d12f3bb4..f0609ff9 100644 --- a/mongoengine/queryset.py +++ b/mongoengine/queryset.py @@ -929,7 +929,7 @@ class QuerySet(object): if not isinstance(doc, self._document): msg = "Some documents inserted aren't instances of %s" % str(self._document) raise OperationError(msg) - if doc.pk: + if doc.pk and not doc._created: msg = "Some documents have ObjectIds use doc.update() instead" raise OperationError(msg) raw.append(doc.to_mongo()) diff --git a/tests/test_queryset.py b/tests/test_queryset.py index 09a48233..aed606d0 100644 --- a/tests/test_queryset.py +++ b/tests/test_queryset.py @@ -591,6 +591,10 @@ class QuerySetTest(unittest.TestCase): self.assertRaises(OperationError, throw_operation_error) + # Test can insert new doc + new_post = Blog(title="code", id=ObjectId()) + Blog.objects.insert(new_post) + # test handles other classes being inserted def throw_operation_error_wrong_doc(): class Author(Document): From 260d9377f537590e3d0c1cfe0dfe08dff5401784 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Mon, 10 Dec 2012 08:26:42 +0000 Subject: [PATCH 19/25] Updated Changelog --- docs/changelog.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 3dff3bd5..5fcf619b 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -5,6 +5,7 @@ Changelog Changes in 0.7.8 ================ +- Added optional encoding and collection config for Django sessions (MongoEngine/mongoengine#180, MongoEngine/mongoengine#181, MongoEngine/mongoengine#183) - Fixed EmailField so can add extra validation (MongoEngine/mongoengine#173, MongoEngine/mongoengine#174, MongoEngine/mongoengine#187) - Fixed bulk inserts can now handle custom pk's (MongoEngine/mongoengine#192) - Added as_pymongo method to return raw or cast results from pymongo (MongoEngine/mongoengine#193) From 90d22c2a28dc0946457565ca250b443c89fbe220 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Mon, 10 Dec 2012 08:50:21 +0000 Subject: [PATCH 20/25] Update AUTHORS & Changelog (MongoEngine/mongoengine#176) --- AUTHORS | 1 + docs/changelog.rst | 1 + 2 files changed, 2 insertions(+) diff --git a/AUTHORS b/AUTHORS index 1c57463b..330256c2 100644 --- a/AUTHORS +++ b/AUTHORS @@ -125,3 +125,4 @@ that much better: * dimonb * Garry Polley * Adrian Scott + * Peter Teichman diff --git a/docs/changelog.rst b/docs/changelog.rst index 5fcf619b..c9535a07 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -5,6 +5,7 @@ Changelog Changes in 0.7.8 ================ +- Fix query chaining with .order_by() (MongoEngine/mongoengine#176) - Added optional encoding and collection config for Django sessions (MongoEngine/mongoengine#180, MongoEngine/mongoengine#181, MongoEngine/mongoengine#183) - Fixed EmailField so can add extra validation (MongoEngine/mongoengine#173, MongoEngine/mongoengine#174, MongoEngine/mongoengine#187) - Fixed bulk inserts can now handle custom pk's (MongoEngine/mongoengine#192) From 9236f365fa95b5006fe50702c29dd118d888f2e5 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Mon, 10 Dec 2012 09:11:31 +0000 Subject: [PATCH 21/25] Fix sequence fields in embedded documents (MongoEngine/mongoengine#166) --- docs/changelog.rst | 1 + mongoengine/fields.py | 13 +++++++++++-- tests/test_fields.py | 22 ++++++++++++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index c9535a07..92770afb 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -5,6 +5,7 @@ Changelog Changes in 0.7.8 ================ +- Fix sequence fields in embedded documents (MongoEngine/mongoengine#166) - Fix query chaining with .order_by() (MongoEngine/mongoengine#176) - Added optional encoding and collection config for Django sessions (MongoEngine/mongoengine#180, MongoEngine/mongoengine#181, MongoEngine/mongoengine#183) - Fixed EmailField so can add extra validation (MongoEngine/mongoengine#173, MongoEngine/mongoengine#174, MongoEngine/mongoengine#187) diff --git a/mongoengine/fields.py b/mongoengine/fields.py index 9c0bedec..3f413b25 100644 --- a/mongoengine/fields.py +++ b/mongoengine/fields.py @@ -1338,7 +1338,7 @@ class SequenceField(IntField): .. versionadded:: 0.5 """ - def __init__(self, collection_name=None, db_alias = None, sequence_name = None, *args, **kwargs): + def __init__(self, collection_name=None, db_alias=None, sequence_name=None, *args, **kwargs): self.collection_name = collection_name or 'mongoengine.counters' self.db_alias = db_alias or DEFAULT_CONNECTION_NAME self.sequence_name = sequence_name @@ -1348,7 +1348,7 @@ class SequenceField(IntField): """ Generate and Increment the counter """ - sequence_name = self.sequence_name or self.owner_document._get_collection_name() + sequence_name = self.get_sequence_name() sequence_id = "%s.%s" % (sequence_name, self.name) collection = get_db(alias=self.db_alias)[self.collection_name] counter = collection.find_and_modify(query={"_id": sequence_id}, @@ -1357,6 +1357,15 @@ class SequenceField(IntField): upsert=True) return counter['next'] + def get_sequence_name(self): + if self.sequence_name: + return self.sequence_name + owner = self.owner_document + if issubclass(owner, Document): + return owner._get_collection_name() + else: + return owner._class_name + def __get__(self, instance, owner): if instance is None: diff --git a/tests/test_fields.py b/tests/test_fields.py index 88e82f17..fdcc3080 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -2175,6 +2175,28 @@ class FieldTest(unittest.TestCase): c = self.db['mongoengine.counters'].find_one({'_id': 'animal.id'}) self.assertEqual(c['next'], 10) + def test_embedded_sequence_field(self): + class Comment(EmbeddedDocument): + id = SequenceField() + content = StringField(required=True) + + class Post(Document): + title = StringField(required=True) + comments = ListField(EmbeddedDocumentField(Comment)) + + self.db['mongoengine.counters'].drop() + Post.drop_collection() + + Post(title="MongoEngine", + comments=[Comment(content="NoSQL Rocks"), + Comment(content="MongoEngine Rocks")]).save() + + c = self.db['mongoengine.counters'].find_one({'_id': 'Comment.id'}) + self.assertEqual(c['next'], 2) + post = Post.objects.first() + self.assertEqual(1, post.comments[0].id) + self.assertEqual(2, post.comments[1].id) + def test_generic_embedded_document(self): class Car(EmbeddedDocument): name = StringField() From 1bc2d2ec37ffe608ad56d7e790d087e6dfeda1bc Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Mon, 10 Dec 2012 09:54:20 +0000 Subject: [PATCH 22/25] Version Bump --- mongoengine/__init__.py | 2 +- python-mongoengine.spec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mongoengine/__init__.py b/mongoengine/__init__.py index 9f1f552f..08cabaa1 100644 --- a/mongoengine/__init__.py +++ b/mongoengine/__init__.py @@ -12,7 +12,7 @@ from signals import * __all__ = (document.__all__ + fields.__all__ + connection.__all__ + queryset.__all__ + signals.__all__) -VERSION = (0, 7, 7) +VERSION = (0, 7, 8) def get_version(): diff --git a/python-mongoengine.spec b/python-mongoengine.spec index 9a376ec7..f175546b 100644 --- a/python-mongoengine.spec +++ b/python-mongoengine.spec @@ -5,7 +5,7 @@ %define srcname mongoengine Name: python-%{srcname} -Version: 0.7.7 +Version: 0.7.8 Release: 1%{?dist} Summary: A Python Document-Object Mapper for working with MongoDB From 6ff1bd9b3cb623bc6c864e0856a7ec37b427fd65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johnny=20Bergstr=C3=B6m?= Date: Mon, 10 Dec 2012 11:01:08 +0100 Subject: [PATCH 23/25] Corrected user guide link in README --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index ae6bd0ec..5eab5021 100644 --- a/README.rst +++ b/README.rst @@ -14,7 +14,7 @@ About MongoEngine is a Python Object-Document Mapper for working with MongoDB. Documentation available at http://mongoengine-odm.rtfd.org - there is currently a `tutorial `_, a `user guide -`_ and an `API reference +`_ and an `API reference `_. Installation From b15c3f6a3f1f54023cb7ca9855db938373f012c2 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Mon, 10 Dec 2012 10:33:55 +0000 Subject: [PATCH 24/25] Update AUTHORS Sorry Jorge! --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 864c95cc..82a1dfa4 100644 --- a/AUTHORS +++ b/AUTHORS @@ -127,3 +127,4 @@ that much better: * Adrian Scott * Peter Teichman * Jakub Kot + * Jorge Bastida From 3b3738b36ba007460a9085472c5013cabc664956 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Mon, 10 Dec 2012 15:16:31 +0000 Subject: [PATCH 25/25] 0.7.9 --- docs/changelog.rst | 4 ++++ mongoengine/__init__.py | 2 +- mongoengine/base.py | 8 +++++--- mongoengine/fields.py | 3 ++- python-mongoengine.spec | 2 +- tests/test_document.py | 16 ++++++++++++++-- tests/test_fields.py | 2 +- 7 files changed, 28 insertions(+), 9 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 92770afb..d93bf13c 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -2,6 +2,10 @@ Changelog ========= +Changes in 0.7.9 +================ +- Better fix handling for old style _types +- Embedded SequenceFields follow collection naming convention Changes in 0.7.8 ================ diff --git a/mongoengine/__init__.py b/mongoengine/__init__.py index 08cabaa1..b67512d7 100644 --- a/mongoengine/__init__.py +++ b/mongoengine/__init__.py @@ -12,7 +12,7 @@ from signals import * __all__ = (document.__all__ + fields.__all__ + connection.__all__ + queryset.__all__ + signals.__all__) -VERSION = (0, 7, 8) +VERSION = (0, 7, 9) def get_version(): diff --git a/mongoengine/base.py b/mongoengine/base.py index 208e0e59..013afe78 100644 --- a/mongoengine/base.py +++ b/mongoengine/base.py @@ -122,9 +122,11 @@ def get_document(name): doc = _document_registry.get(name, None) if not doc: # Possible old style name - end = name.split('.')[-1] - possible_match = [k for k in _document_registry.keys() if k == end] - if len(possible_match) == 1 and end != name: + single_end = name.split('.')[-1] + compound_end = '.%s' % single_end + possible_match = [k for k in _document_registry.keys() + if k.endswith(compound_end) or k == single_end] + if len(possible_match) == 1: doc = _document_registry.get(possible_match.pop(), None) if not doc: raise NotRegistered(""" diff --git a/mongoengine/fields.py b/mongoengine/fields.py index 213c2148..de484a1d 100644 --- a/mongoengine/fields.py +++ b/mongoengine/fields.py @@ -1364,7 +1364,8 @@ class SequenceField(IntField): if issubclass(owner, Document): return owner._get_collection_name() else: - return owner._class_name + return ''.join('_%s' % c if c.isupper() else c + for c in owner._class_name).strip('_').lower() def __get__(self, instance, owner): diff --git a/python-mongoengine.spec b/python-mongoengine.spec index f175546b..b1ec3361 100644 --- a/python-mongoengine.spec +++ b/python-mongoengine.spec @@ -5,7 +5,7 @@ %define srcname mongoengine Name: python-%{srcname} -Version: 0.7.8 +Version: 0.7.9 Release: 1%{?dist} Summary: A Python Document-Object Mapper for working with MongoDB diff --git a/tests/test_document.py b/tests/test_document.py index a09aaeca..cd0ab8fb 100644 --- a/tests/test_document.py +++ b/tests/test_document.py @@ -15,7 +15,7 @@ from datetime import datetime from tests.fixtures import Base, Mixin, PickleEmbedded, PickleTest from mongoengine import * -from mongoengine.base import NotRegistered, InvalidDocumentError +from mongoengine.base import NotRegistered, InvalidDocumentError, get_document from mongoengine.queryset import InvalidQueryError from mongoengine.connection import get_db, get_connection @@ -1336,7 +1336,6 @@ class DocumentTest(unittest.TestCase): User.drop_collection() - def test_document_not_registered(self): class Place(Document): @@ -1361,6 +1360,19 @@ class DocumentTest(unittest.TestCase): print Place.objects.all() self.assertRaises(NotRegistered, query_without_importing_nice_place) + def test_document_registry_regressions(self): + + class Location(Document): + name = StringField() + meta = {'allow_inheritance': True} + + class Area(Location): + location = ReferenceField('Location', dbref=True) + + Location.drop_collection() + + self.assertEquals(Area, get_document("Area")) + self.assertEquals(Area, get_document("Location.Area")) def test_creation(self): """Ensure that document may be created using keyword arguments. diff --git a/tests/test_fields.py b/tests/test_fields.py index 0483519e..28af1b23 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -2201,7 +2201,7 @@ class FieldTest(unittest.TestCase): comments=[Comment(content="NoSQL Rocks"), Comment(content="MongoEngine Rocks")]).save() - c = self.db['mongoengine.counters'].find_one({'_id': 'Comment.id'}) + c = self.db['mongoengine.counters'].find_one({'_id': 'comment.id'}) self.assertEqual(c['next'], 2) post = Post.objects.first() self.assertEqual(1, post.comments[0].id)