From 601f0eb1682ebd9c9eb67f1b68b76bdd2cf3d8c7 Mon Sep 17 00:00:00 2001 From: Max Countryman Date: Fri, 20 Jul 2012 19:12:43 -0700 Subject: [PATCH 1/6] Correcting typo in DynamicField docstring --- mongoengine/fields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mongoengine/fields.py b/mongoengine/fields.py index 01d50c1c..e94fe309 100644 --- a/mongoengine/fields.py +++ b/mongoengine/fields.py @@ -483,7 +483,7 @@ class GenericEmbeddedDocumentField(BaseField): class DynamicField(BaseField): - """A tryly dynamic field type capable of handling different and varying + """A truly dynamic field type capable of handling different and varying types of data. Used by :class:`~mongoengine.DynamicDocument` to handle dynamic data""" From 598ffd3e5c107990dd41995d0bbb21bf13f67fb5 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Mon, 23 Jul 2012 15:32:02 +0100 Subject: [PATCH 2/6] Fixed documentation --- docs/changelog.rst | 2 +- docs/guide/defining-documents.rst | 29 +++++++++++++++++++++++++++++ mongoengine/fields.py | 6 ++++-- mongoengine/queryset.py | 8 ++++---- 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 19d21b87..fa0f3611 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -27,7 +27,7 @@ Changes in 0.6.14 - Added support for add_to_set and each Changes in 0.6.13 -================ +================= - Fixed EmbeddedDocument db_field validation issue - Fixed StringField unicode issue - Fixes __repr__ modifying the cursor diff --git a/docs/guide/defining-documents.rst b/docs/guide/defining-documents.rst index 726ce3b7..b4facbda 100644 --- a/docs/guide/defining-documents.rst +++ b/docs/guide/defining-documents.rst @@ -259,6 +259,35 @@ as the constructor's argument:: content = StringField() +.. _one-to-many-with-listfields: + +One to Many with ListFields +''''''''''''''''''''''''''' + +If you are implementing a one to many relationship via a list of references, +then the references are stored as DBRefs and to query you need to pass an +instance of the object to the query:: + + class User(Document): + name = StringField() + + class Page(Document): + content = StringField() + authors = ListField(ReferenceField(User)) + + bob = User(name="Bob Jones").save() + john = User(name="John Smith").save() + + Page(content="Test Page", authors=[bob, john]).save() + Page(content="Another Page", authors=[john]).save() + + # Find all pages Bob authored + Page.objects(authors__in=[bob]) + + # Find all pages that both Bob and John have authored + Page.objects(authors__all=[bob, john]) + + Dealing with deletion of referred documents ''''''''''''''''''''''''''''''''''''''''''' By default, MongoDB doesn't check the integrity of your data, so deleting diff --git a/mongoengine/fields.py b/mongoengine/fields.py index 01d50c1c..ba8ff2dd 100644 --- a/mongoengine/fields.py +++ b/mongoengine/fields.py @@ -169,7 +169,7 @@ class IntField(BaseField): def prepare_query_value(self, op, value): if value is None: return value - + return int(value) @@ -199,7 +199,7 @@ class FloatField(BaseField): def prepare_query_value(self, op, value): if value is None: return value - + return float(value) @@ -530,6 +530,8 @@ class ListField(ComplexBaseField): """A list field that wraps a standard field, allowing multiple instances of the field to be used as a list in the database. + If using with ReferenceFields see: :ref:`one-to-many-with-listfields` + .. note:: Required means it cannot be empty - as the default for ListFields is [] """ diff --git a/mongoengine/queryset.py b/mongoengine/queryset.py index 6499c3e0..f68545b2 100644 --- a/mongoengine/queryset.py +++ b/mongoengine/queryset.py @@ -806,9 +806,9 @@ class QuerySet(object): keyword argument called :attr:`defaults`. .. note:: This requires two separate operations and therefore a - race condition exists. Because there are no transactions in mongoDB - other approaches should be investigated, to ensure you don't - accidently duplicate data when using this method. + race condition exists. Because there are no transactions in mongoDB + other approaches should be investigated, to ensure you don't + accidently duplicate data when using this method. :param write_options: optional extra keyword arguments used if we have to create a new document. @@ -816,8 +816,8 @@ class QuerySet(object): :param auto_save: if the object is to be saved automatically if not found. + .. versionchanged:: 0.6 - added `auto_save` .. versionadded:: 0.3 - .. versionupdated:: 0.6 - added `auto_save` """ defaults = query.get('defaults', {}) if 'defaults' in query: From 1304f2721f7850b223950edb85ec7c141255176c Mon Sep 17 00:00:00 2001 From: Chris Faulkner Date: Tue, 24 Jul 2012 14:06:43 -0700 Subject: [PATCH 3/6] Proper syntax for RST notes (so they actually render). --- mongoengine/document.py | 2 +- mongoengine/fields.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mongoengine/document.py b/mongoengine/document.py index 27792780..f8bf769d 100644 --- a/mongoengine/document.py +++ b/mongoengine/document.py @@ -375,7 +375,7 @@ class DynamicDocument(Document): :class:`~mongoengine.DynamicField` and data can be attributed to that field. - ..note:: + .. note:: There is one caveat on Dynamic Documents: fields cannot start with `_` """ diff --git a/mongoengine/fields.py b/mongoengine/fields.py index ba8ff2dd..43f58e58 100644 --- a/mongoengine/fields.py +++ b/mongoengine/fields.py @@ -451,7 +451,7 @@ class GenericEmbeddedDocumentField(BaseField): Only valid values are subclasses of :class:`~mongoengine.EmbeddedDocument`. - ..note :: You can use the choices param to limit the acceptable + .. note:: You can use the choices param to limit the acceptable EmbeddedDocument types """ @@ -768,10 +768,10 @@ class GenericReferenceField(BaseField): """A reference to *any* :class:`~mongoengine.document.Document` subclass that will be automatically dereferenced on access (lazily). - ..note :: Any documents used as a generic reference must be registered in the + .. note:: Any documents used as a generic reference must be registered in the document registry. Importing the model will automatically register it. - ..note :: You can use the choices param to limit the acceptable Document types + .. note:: You can use the choices param to limit the acceptable Document types .. versionadded:: 0.3 """ From 6459d4c0b60229edcd4d562898833f221d00ebf4 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Wed, 25 Jul 2012 14:55:10 +0100 Subject: [PATCH 4/6] Fixed issue with custom queryset manager expecting explict variable names If using / expecting kwargs you have to call the queryset manager explicitly. --- docs/changelog.rst | 4 ++++ mongoengine/queryset.py | 4 ++-- tests/test_queryset.py | 30 +++++++++++++++--------------- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index fa0f3611..22abe181 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -3,6 +3,10 @@ Changelog ========= +Changes in 0.6.17 +================= +- Fixed issue with custom queryset manager expecting explict variable names + Changes in 0.6.16 ================= - Fixed issue where db_alias wasn't inherited diff --git a/mongoengine/queryset.py b/mongoengine/queryset.py index f68545b2..0d1d95b7 100644 --- a/mongoengine/queryset.py +++ b/mongoengine/queryset.py @@ -1882,9 +1882,9 @@ class QuerySetManager(object): queryset = queryset_class(owner, owner._get_collection()) if self.get_queryset: var_names = self.get_queryset.func_code.co_varnames - if var_names == ('queryset',): + if len(var_names) == 1: queryset = self.get_queryset(queryset) - elif var_names == ('doc_cls', 'queryset',): + elif len(var_names) == 2: queryset = self.get_queryset(owner, queryset) else: queryset = partial(self.get_queryset, owner, queryset) diff --git a/tests/test_queryset.py b/tests/test_queryset.py index 1bac6a97..c1abb4d7 100644 --- a/tests/test_queryset.py +++ b/tests/test_queryset.py @@ -2228,28 +2228,28 @@ class QuerySetTest(unittest.TestCase): date = DateTimeField(default=datetime.now) @queryset_manager - def objects(doc_cls, queryset): - return queryset(deleted=False) + def objects(cls, qryset): + return qryset(deleted=False) @queryset_manager - def music_posts(doc_cls, queryset): - return queryset(tags='music', deleted=False).order_by('-date') + def music_posts(doc_cls, queryset, deleted=False): + return queryset(tags='music', + deleted=deleted).order_by('date') BlogPost.drop_collection() - post1 = BlogPost(tags=['music', 'film']) - post1.save() - post2 = BlogPost(tags=['music']) - post2.save() - post3 = BlogPost(tags=['film', 'actors']) - post3.save() - post4 = BlogPost(tags=['film', 'actors'], deleted=True) - post4.save() + post1 = BlogPost(tags=['music', 'film']).save() + post2 = BlogPost(tags=['music']).save() + post3 = BlogPost(tags=['film', 'actors']).save() + post4 = BlogPost(tags=['film', 'actors', 'music'], deleted=True).save() - self.assertEqual([p.id for p in BlogPost.objects], + self.assertEqual([p.id for p in BlogPost.objects()], [post1.id, post2.id, post3.id]) - self.assertEqual([p.id for p in BlogPost.music_posts], - [post2.id, post1.id]) + self.assertEqual([p.id for p in BlogPost.music_posts()], + [post1.id, post2.id]) + + self.assertEqual([p.id for p in BlogPost.music_posts(True)], + [post4.id]) BlogPost.drop_collection() From 24fd1acce68341361331e033d1692d61a936f871 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Thu, 26 Jul 2012 14:14:10 +0100 Subject: [PATCH 5/6] 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 ad12479d..2b6af433 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, 6, 16) +VERSION = (0, 6, 17) def get_version(): diff --git a/python-mongoengine.spec b/python-mongoengine.spec index 03093b36..ea2c6040 100644 --- a/python-mongoengine.spec +++ b/python-mongoengine.spec @@ -5,7 +5,7 @@ %define srcname mongoengine Name: python-%{srcname} -Version: 0.6.16 +Version: 0.6.17 Release: 1%{?dist} Summary: A Python Document-Object Mapper for working with MongoDB From 6526923345efd768044c4fba770a8a3ada161a40 Mon Sep 17 00:00:00 2001 From: Ross Lawley Date: Thu, 26 Jul 2012 16:00:32 +0100 Subject: [PATCH 6/6] Fixed recursion loading bug in _get_changed_fields fixes hmarr/mongoengine#548 --- docs/changelog.rst | 4 +++ mongoengine/__init__.py | 2 +- mongoengine/base.py | 3 ++- python-mongoengine.spec | 2 +- tests/test_queryset.py | 58 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 66 insertions(+), 3 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 22abe181..97926f8d 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -3,6 +3,10 @@ Changelog ========= +Changes in 0.6.18 +================= +- Fixed recursion loading bug in _get_changed_fields + Changes in 0.6.17 ================= - Fixed issue with custom queryset manager expecting explict variable names diff --git a/mongoengine/__init__.py b/mongoengine/__init__.py index 2b6af433..5f4f7f1d 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, 6, 17) +VERSION = (0, 6, 18) def get_version(): diff --git a/mongoengine/base.py b/mongoengine/base.py index 09768c4a..6fb26cb7 100644 --- a/mongoengine/base.py +++ b/mongoengine/base.py @@ -1012,9 +1012,10 @@ Invalid data to create a `%s` instance.\n%s""".strip() % (cls._class_name, error field_list.update(self._dynamic_fields) for field_name in field_list: + db_field_name = self._db_field_map.get(field_name, field_name) key = '%s.' % db_field_name - field = getattr(self, field_name, None) + field = self._data.get(field_name, None) if hasattr(field, 'id'): if field.id in inspected: continue diff --git a/python-mongoengine.spec b/python-mongoengine.spec index ea2c6040..4dfbeccc 100644 --- a/python-mongoengine.spec +++ b/python-mongoengine.spec @@ -5,7 +5,7 @@ %define srcname mongoengine Name: python-%{srcname} -Version: 0.6.17 +Version: 0.6.18 Release: 1%{?dist} Summary: A Python Document-Object Mapper for working with MongoDB diff --git a/tests/test_queryset.py b/tests/test_queryset.py index c1abb4d7..e623790f 100644 --- a/tests/test_queryset.py +++ b/tests/test_queryset.py @@ -579,6 +579,64 @@ class QuerySetTest(unittest.TestCase): Blog.objects.insert([blog2, blog3], write_options={'continue_on_error': True}) self.assertEqual(Blog.objects.count(), 3) + def test_get_changed_fields_query_count(self): + + class Person(Document): + name = StringField() + owns = ListField(ReferenceField('Organization')) + projects = ListField(ReferenceField('Project')) + + class Organization(Document): + name = StringField() + owner = ReferenceField('Person') + employees = ListField(ReferenceField('Person')) + + class Project(Document): + name = StringField() + + Person.drop_collection() + Organization.drop_collection() + Project.drop_collection() + + r1 = Project(name="r1").save() + r2 = Project(name="r2").save() + r3 = Project(name="r3").save() + p1 = Person(name="p1", projects=[r1, r2]).save() + p2 = Person(name="p2", projects=[r2]).save() + o1 = Organization(name="o1", employees=[p1]).save() + + with query_counter() as q: + self.assertEqual(q, 0) + + fresh_o1 = Organization.objects.get(id=o1.id) + self.assertEqual(1, q) + fresh_o1._get_changed_fields() + self.assertEqual(1, q) + + with query_counter() as q: + self.assertEqual(q, 0) + + fresh_o1 = Organization.objects.get(id=o1.id) + fresh_o1.save() + + self.assertEquals(q, 2) + + with query_counter() as q: + self.assertEqual(q, 0) + + fresh_o1 = Organization.objects.get(id=o1.id) + fresh_o1.save(cascade=False) + + self.assertEquals(q, 2) + + with query_counter() as q: + self.assertEqual(q, 0) + + fresh_o1 = Organization.objects.get(id=o1.id) + fresh_o1.employees.append(p2) + fresh_o1.save(cascade=False) + + self.assertEquals(q, 3) def test_slave_okay(self): """Ensures that a query can take slave_okay syntax