From 170693cf0bf9b114ef9a032739c5ebf5d53a56da Mon Sep 17 00:00:00 2001 From: Clay McClure Date: Thu, 19 Jun 2014 19:33:46 -0400 Subject: [PATCH 1/7] Follow ReferenceFields in EmbeddedDocuments with select_related For the following structure: class Playlist(Document): items = ListField(EmbeddedDocumentField("PlaylistItem")) class PlaylistItem(EmbeddedDocument): song = ReferenceField("Song") class Song(Document): title = StringField() this patch prevents the N+1 queries otherwise required to fetch all the `Song` instances referenced by all the `PlaylistItem`s. --- docs/changelog.rst | 1 + mongoengine/dereference.py | 4 ++-- tests/test_dereference.py | 26 +++++++++++++++++++++++++- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 9a55b91b..52347d01 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -6,6 +6,7 @@ Changelog Changes in 0.9.X - DEV ====================== +- Follow ReferenceFields in EmbeddedDocuments with select_related #690 - Added preliminary support for text indexes #680 - Added `elemMatch` operator as well - `match` is too obscure #653 - Added support for progressive JPEG #486 #548 diff --git a/mongoengine/dereference.py b/mongoengine/dereference.py index 18235b96..f9c8ecd6 100644 --- a/mongoengine/dereference.py +++ b/mongoengine/dereference.py @@ -95,7 +95,7 @@ class DeReference(object): # Recursively find dbreferences depth += 1 for k, item in iterator: - if isinstance(item, Document): + if isinstance(item, (Document, EmbeddedDocument)): for field_name, field in item._fields.iteritems(): v = item._data.get(field_name, None) if isinstance(v, (DBRef)): @@ -202,7 +202,7 @@ class DeReference(object): if k in self.object_map and not is_list: data[k] = self.object_map[k] - elif isinstance(v, Document): + elif isinstance(v, (Document, EmbeddedDocument)): for field_name, field in v._fields.iteritems(): v = data[k]._data.get(field_name, None) if isinstance(v, (DBRef)): diff --git a/tests/test_dereference.py b/tests/test_dereference.py index dc416007..c37ada59 100644 --- a/tests/test_dereference.py +++ b/tests/test_dereference.py @@ -1219,6 +1219,30 @@ class FieldTest(unittest.TestCase): page = Page.objects.first() self.assertEqual(page.tags[0], page.posts[0].tags[0]) + def test_select_related_follows_embedded_referencefields(self): + class Playlist(Document): + items = ListField(EmbeddedDocumentField("PlaylistItem")) + + class PlaylistItem(EmbeddedDocument): + song = ReferenceField("Song") + + class Song(Document): + title = StringField() + + Playlist.drop_collection() + Song.drop_collection() + + songs = [Song.objects.create(title="song %d" % i) for i in range(3)] + items = [PlaylistItem(song=song) for song in songs] + playlist = Playlist.objects.create(items=items) + + with query_counter() as q: + self.assertEqual(q, 0) + + playlist = Playlist.objects.first().select_related() + songs = [item.song for item in playlist.items] + + self.assertEqual(q, 2) + if __name__ == '__main__': unittest.main() - From f865c5de90ed5a81bf968375cb80bd4abcb7c8ad Mon Sep 17 00:00:00 2001 From: Aleksey Porfirov Date: Fri, 4 Jul 2014 22:30:29 +0400 Subject: [PATCH 2/7] Fix tests for Django 1.7 --- tests/test_django.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_django.py b/tests/test_django.py index 56a61640..1741e0f8 100644 --- a/tests/test_django.py +++ b/tests/test_django.py @@ -7,6 +7,7 @@ from mongoengine import * from mongoengine.django.shortcuts import get_document_or_404 +import django from django.http import Http404 from django.template import Context, Template from django.conf import settings @@ -19,6 +20,10 @@ settings.configure( AUTHENTICATION_BACKENDS = ('mongoengine.django.auth.MongoEngineBackend',) ) +# For Django >= 1.7 +if hasattr(django, 'setup'): + django.setup() + try: from django.contrib.auth import authenticate, get_user_model from mongoengine.django.auth import User From b2a2735034d64381ca93d3566c0573ee34d19bf4 Mon Sep 17 00:00:00 2001 From: Aleksey Porfirov Date: Fri, 4 Jul 2014 22:32:07 +0400 Subject: [PATCH 3/7] Update AUTHORS --- AUTHORS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index b2b7c05d..d56e9910 100644 --- a/AUTHORS +++ b/AUTHORS @@ -142,7 +142,7 @@ that much better: * Pete Campton * Martyn Smith * Marcelo Anton - * Aleksey Porfirov + * Aleksey Porfirov (https://github.com/lexqt) * Nicolas Trippar * Manuel Hermann * Gustavo Gawryszewski From 0d81e7933e760f81365bd4fa58175e2ee85ceef9 Mon Sep 17 00:00:00 2001 From: Aleksey Porfirov Date: Sat, 5 Jul 2014 00:06:10 +0400 Subject: [PATCH 4/7] Prevent accessing not yet configured settings in django.MongoTestCase --- mongoengine/django/tests.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mongoengine/django/tests.py b/mongoengine/django/tests.py index 1fab6fc3..c7af42a3 100644 --- a/mongoengine/django/tests.py +++ b/mongoengine/django/tests.py @@ -26,7 +26,11 @@ class MongoTestCase(TestCase): """ TestCase class that clear the collection between the tests """ - db_name = 'test_%s' % settings.MONGO_DATABASE_NAME + + @property + def db_name(self): + return 'test_%s' % settings.MONGO_DATABASE_NAME + def __init__(self, methodName='runtest'): self.db = connect(self.db_name).get_db() super(MongoTestCase, self).__init__(methodName) From c4b319691718c771fe3da8a011c8291e35c60b6c Mon Sep 17 00:00:00 2001 From: Aleksey Porfirov Date: Sat, 5 Jul 2014 21:13:25 +0400 Subject: [PATCH 5/7] Fix MongoTestCase and add test for it --- mongoengine/django/tests.py | 30 +++++++++++++++++------------- tests/test_django.py | 11 +++++++++-- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/mongoengine/django/tests.py b/mongoengine/django/tests.py index c7af42a3..620e6ba6 100644 --- a/mongoengine/django/tests.py +++ b/mongoengine/django/tests.py @@ -1,38 +1,31 @@ #coding: utf-8 -from nose.plugins.skip import SkipTest -from mongoengine.python_support import PY3 from mongoengine import connect +from mongoengine.connection import get_db +from mongoengine.python_support import PY3 try: from django.test import TestCase - from django.conf import settings except Exception as err: if PY3: from unittest import TestCase - # Dummy value so no error - class settings: - MONGO_DATABASE_NAME = 'dummy' else: raise err class MongoTestCase(TestCase): - - def setUp(self): - if PY3: - raise SkipTest('django does not have Python 3 support') - """ TestCase class that clear the collection between the tests """ @property def db_name(self): - return 'test_%s' % settings.MONGO_DATABASE_NAME + from django.conf import settings + return 'test_%s' % getattr(settings, 'MONGO_DATABASE_NAME', 'dummy') def __init__(self, methodName='runtest'): - self.db = connect(self.db_name).get_db() + connect(self.db_name) + self.db = get_db() super(MongoTestCase, self).__init__(methodName) def _post_teardown(self): @@ -41,3 +34,14 @@ class MongoTestCase(TestCase): if collection == 'system.indexes': continue self.db.drop_collection(collection) + + # prevent standard db init + + def _databases_names(self, *args, **kwargs): + return [] + + def _fixture_setup(self): + pass + + def _fixture_teardown(self): + pass diff --git a/tests/test_django.py b/tests/test_django.py index 1741e0f8..de49e8ac 100644 --- a/tests/test_django.py +++ b/tests/test_django.py @@ -2,9 +2,8 @@ import sys sys.path[0:0] = [""] import unittest from nose.plugins.skip import SkipTest + from mongoengine import * - - from mongoengine.django.shortcuts import get_document_or_404 import django @@ -37,6 +36,7 @@ except Exception: DJ15 = False from django.contrib.sessions.tests import SessionTestsMixin from mongoengine.django.sessions import SessionStore, MongoSession +from mongoengine.django.tests import MongoTestCase from datetime import tzinfo, timedelta ZERO = timedelta(0) @@ -298,5 +298,12 @@ class MongoAuthTest(unittest.TestCase): db_user = User.objects.get(username='user') self.assertEqual(user.id, db_user.id) + +class MongoTestCaseTest(MongoTestCase): + def test_mongo_test_case(self): + # test __init__ and teardown in MongoTestCase + pass + + if __name__ == '__main__': unittest.main() From 3e54da03e26354e7b8c82df25b1677ee0da05313 Mon Sep 17 00:00:00 2001 From: Aleksey Porfirov Date: Sat, 5 Jul 2014 21:35:31 +0400 Subject: [PATCH 6/7] Fix MongoTestCase and add test for it --- mongoengine/django/tests.py | 26 +++++--------------------- tests/test_django.py | 3 +-- 2 files changed, 6 insertions(+), 23 deletions(-) diff --git a/mongoengine/django/tests.py b/mongoengine/django/tests.py index 620e6ba6..d7fc3939 100644 --- a/mongoengine/django/tests.py +++ b/mongoengine/django/tests.py @@ -1,16 +1,9 @@ #coding: utf-8 +from unittest import TestCase + from mongoengine import connect from mongoengine.connection import get_db -from mongoengine.python_support import PY3 - -try: - from django.test import TestCase -except Exception as err: - if PY3: - from unittest import TestCase - else: - raise err class MongoTestCase(TestCase): @@ -28,20 +21,11 @@ class MongoTestCase(TestCase): self.db = get_db() super(MongoTestCase, self).__init__(methodName) - def _post_teardown(self): - super(MongoTestCase, self)._post_teardown() + def dropCollections(self): for collection in self.db.collection_names(): if collection == 'system.indexes': continue self.db.drop_collection(collection) - # prevent standard db init - - def _databases_names(self, *args, **kwargs): - return [] - - def _fixture_setup(self): - pass - - def _fixture_teardown(self): - pass + def tearDown(self): + self.dropCollections() diff --git a/tests/test_django.py b/tests/test_django.py index de49e8ac..7badbd64 100644 --- a/tests/test_django.py +++ b/tests/test_django.py @@ -301,8 +301,7 @@ class MongoAuthTest(unittest.TestCase): class MongoTestCaseTest(MongoTestCase): def test_mongo_test_case(self): - # test __init__ and teardown in MongoTestCase - pass + self.db.dummy_collection.insert({'collection': 'will be dropped'}) if __name__ == '__main__': From 03dcfb5c4bf7acbddef353b7311c1178b9625cfd Mon Sep 17 00:00:00 2001 From: Aleksey Porfirov Date: Sun, 6 Jul 2014 12:27:34 +0400 Subject: [PATCH 7/7] Update changelog --- docs/changelog.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 52347d01..f3657f56 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -6,6 +6,7 @@ Changelog Changes in 0.9.X - DEV ====================== +- Fixed tests for Django 1.7 #696 - Follow ReferenceFields in EmbeddedDocuments with select_related #690 - Added preliminary support for text indexes #680 - Added `elemMatch` operator as well - `match` is too obscure #653