From d65ce6fc2c957d5b297fea724ad35ab0b34a138b Mon Sep 17 00:00:00 2001 From: mrigal Date: Fri, 28 Nov 2014 13:54:33 +0100 Subject: [PATCH 1/5] fixed bug for queryset.distinct to work also on embedded documents, not just on lists of embedded documents --- mongoengine/queryset/base.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mongoengine/queryset/base.py b/mongoengine/queryset/base.py index e9e87cfc..f41fed39 100644 --- a/mongoengine/queryset/base.py +++ b/mongoengine/queryset/base.py @@ -763,8 +763,7 @@ class BaseQuerySet(object): # We may need to cast to the correct type eg. # ListField(EmbeddedDocumentField) - doc_field = getattr( - self._document._fields.get(field), "field", None) + doc_field = getattr(self._document._fields.get(field), "field", self._document._fields.get(field)) instance = getattr(doc_field, "document_type", False) EmbeddedDocumentField = _import_class('EmbeddedDocumentField') GenericEmbeddedDocumentField = _import_class( From a03262fc011e86630d046e99993ba32397f314c2 Mon Sep 17 00:00:00 2001 From: mrigal Date: Fri, 28 Nov 2014 16:23:23 +0100 Subject: [PATCH 2/5] implemented ability to return instances and not simple dicts for distinct on subdocuments --- mongoengine/queryset/base.py | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/mongoengine/queryset/base.py b/mongoengine/queryset/base.py index f41fed39..0a4b161e 100644 --- a/mongoengine/queryset/base.py +++ b/mongoengine/queryset/base.py @@ -761,13 +761,29 @@ class BaseQuerySet(object): distinct = self._dereference(queryset._cursor.distinct(field), 1, name=field, instance=self._document) - # We may need to cast to the correct type eg. - # ListField(EmbeddedDocumentField) - doc_field = getattr(self._document._fields.get(field), "field", self._document._fields.get(field)) - instance = getattr(doc_field, "document_type", False) + doc_field = self._document._fields.get(field.split('.', 1)[0]) + instance = False + # We may need to cast to the correct type eg. ListField(EmbeddedDocumentField) EmbeddedDocumentField = _import_class('EmbeddedDocumentField') - GenericEmbeddedDocumentField = _import_class( - 'GenericEmbeddedDocumentField') + ListField = _import_class('ListField') + GenericEmbeddedDocumentField = _import_class('GenericEmbeddedDocumentField') + if isinstance(doc_field, ListField): + doc_field = getattr(doc_field, "field", doc_field) + if isinstance(doc_field, (EmbeddedDocumentField, GenericEmbeddedDocumentField)): + instance = getattr(doc_field, "document_type", False) + # handle distinct on subdocuments + if '.' in field: + for field_part in field.split('.')[1:]: + # if looping on embedded document, get the document type instance + if instance and isinstance(doc_field, (EmbeddedDocumentField, GenericEmbeddedDocumentField)): + doc_field = instance + # now get the subdocument + doc_field = getattr(doc_field, field_part, doc_field) + # We may need to cast to the correct type eg. ListField(EmbeddedDocumentField) + if isinstance(doc_field, ListField): + doc_field = getattr(doc_field, "field", doc_field) + if isinstance(doc_field, (EmbeddedDocumentField, GenericEmbeddedDocumentField)): + instance = getattr(doc_field, "document_type", False) if instance and isinstance(doc_field, (EmbeddedDocumentField, GenericEmbeddedDocumentField)): distinct = [instance(**doc) for doc in distinct] From 531fa30b691bebbd98a6ab9884e0afbbbb6cd5b8 Mon Sep 17 00:00:00 2001 From: mrigal Date: Mon, 1 Dec 2014 18:20:29 +0100 Subject: [PATCH 3/5] added test for capacity to get distinct on subdocument of a list --- tests/queryset/queryset.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/queryset/queryset.py b/tests/queryset/queryset.py index 703728fe..92b820cf 100644 --- a/tests/queryset/queryset.py +++ b/tests/queryset/queryset.py @@ -2878,6 +2878,36 @@ class QuerySetTest(unittest.TestCase): self.assertEqual(authors, [mark_twain, john_tolkien]) + def test_distinct_ListField_EmbeddedDocumentField_EmbeddedDocumentField(self): + + class Country(EmbeddedDocument): + country_name = StringField() + + class Author(EmbeddedDocument): + name = StringField() + country = EmbeddedDocumentField(Country) + + class Book(Document): + title = StringField() + authors = ListField(EmbeddedDocumentField(Author)) + + Book.drop_collection() + + scotland = Country(country_name="Scotland") + tibet = Country(country_name="Tibet") + + mark_twain = Author(name="Mark Twain", country=scotland) + john_tolkien = Author(name="John Ronald Reuel Tolkien", country=tibet) + + book = Book(title="Tom Sawyer", authors=[mark_twain]).save() + book = Book( + title="The Lord of the Rings", authors=[john_tolkien]).save() + book = Book( + title="The Stories", authors=[mark_twain, john_tolkien]).save() + country_list = Book.objects.distinct("authors.country") + + self.assertEqual(country_list, [scotland, tibet]) + def test_distinct_ListField_ReferenceField(self): class Foo(Document): bar_lst = ListField(ReferenceField('Bar')) From e5a636a159b75a5672b90c021a1a2aa8c6a56c69 Mon Sep 17 00:00:00 2001 From: Omer Katz Date: Wed, 3 Dec 2014 11:09:05 +0200 Subject: [PATCH 4/5] Added a test that verifies distinct operations on nested embedded documents. --- tests/queryset/queryset.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/tests/queryset/queryset.py b/tests/queryset/queryset.py index 92b820cf..a21c561d 100644 --- a/tests/queryset/queryset.py +++ b/tests/queryset/queryset.py @@ -2879,9 +2879,12 @@ class QuerySetTest(unittest.TestCase): self.assertEqual(authors, [mark_twain, john_tolkien]) def test_distinct_ListField_EmbeddedDocumentField_EmbeddedDocumentField(self): - + class Continent(EmbeddedDocument): + continent_name = StringField() + class Country(EmbeddedDocument): country_name = StringField() + continent = EmbeddedDocumentField(Continent) class Author(EmbeddedDocument): name = StringField() @@ -2893,8 +2896,11 @@ class QuerySetTest(unittest.TestCase): Book.drop_collection() - scotland = Country(country_name="Scotland") - tibet = Country(country_name="Tibet") + europe = Continent(continent_name='europe') + asia = Continent(continent_name='asia') + + scotland = Country(country_name="Scotland", continent=europe) + tibet = Country(country_name="Tibet", continent=asia) mark_twain = Author(name="Mark Twain", country=scotland) john_tolkien = Author(name="John Ronald Reuel Tolkien", country=tibet) @@ -2907,6 +2913,10 @@ class QuerySetTest(unittest.TestCase): country_list = Book.objects.distinct("authors.country") self.assertEqual(country_list, [scotland, tibet]) + + continent_list = Book.objects.distinct("authors.country.continent") + + self.assertEqual(continent_list, [asia, europe]) def test_distinct_ListField_ReferenceField(self): class Foo(Document): From 8aaa5951cab68b0c88ec04b892de014ffe2a6930 Mon Sep 17 00:00:00 2001 From: mrigal Date: Wed, 3 Dec 2014 13:13:07 +0100 Subject: [PATCH 5/5] fixed order of list for the test to pass --- tests/queryset/queryset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/queryset/queryset.py b/tests/queryset/queryset.py index a21c561d..6670029d 100644 --- a/tests/queryset/queryset.py +++ b/tests/queryset/queryset.py @@ -2916,7 +2916,7 @@ class QuerySetTest(unittest.TestCase): continent_list = Book.objects.distinct("authors.country.continent") - self.assertEqual(continent_list, [asia, europe]) + self.assertEqual(continent_list, [europe, asia]) def test_distinct_ListField_ReferenceField(self): class Foo(Document):