From 63d55cb797cf835b34db2bbb3bc7a23d0235e6d7 Mon Sep 17 00:00:00 2001 From: Eremeev Danil Date: Thu, 9 Apr 2015 16:23:28 +0500 Subject: [PATCH 1/3] solution for #949 --- mongoengine/queryset/transform.py | 3 +++ tests/queryset/queryset.py | 37 +++++++++++++++++++++---------- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/mongoengine/queryset/transform.py b/mongoengine/queryset/transform.py index 007cf865..b08d450a 100644 --- a/mongoengine/queryset/transform.py +++ b/mongoengine/queryset/transform.py @@ -44,6 +44,9 @@ def query(_doc_cls=None, _field_operation=False, **query): if len(parts) > 1 and parts[-1] in MATCH_OPERATORS: op = parts.pop() + if len(parts) > 1 and not parts[-1]: + parts.pop() + negate = False if len(parts) > 1 and parts[-1] == 'not': parts.pop() diff --git a/tests/queryset/queryset.py b/tests/queryset/queryset.py index ac282c44..5d21cb0c 100644 --- a/tests/queryset/queryset.py +++ b/tests/queryset/queryset.py @@ -1328,7 +1328,7 @@ class QuerySetTest(unittest.TestCase): self.assertEqual(1, BlogPost.objects.count()) def test_reverse_delete_rule_cascade_on_abstract_document(self): - """Ensure cascading deletion of referring documents from the database + """Ensure cascading deletion of referring documents from the database does not fail on abstract document. """ class AbstractBlogPost(Document): @@ -1350,7 +1350,7 @@ class QuerySetTest(unittest.TestCase): self.assertEqual(3, BlogPost.objects.count()) self.Person.objects(name='Test User').delete() - self.assertEqual(1, BlogPost.objects.count()) + self.assertEqual(1, BlogPost.objects.count()) def test_reverse_delete_rule_cascade_self_referencing(self): """Ensure self-referencing CASCADE deletes do not result in infinite @@ -1411,8 +1411,8 @@ class QuerySetTest(unittest.TestCase): self.assertEqual(1, BlogPost.objects.count()) self.assertEqual(None, BlogPost.objects.first().category) - def test_reverse_delete_rule_nullify_on_abstract_document(self): - """Ensure nullification of references to deleted documents when + def test_reverse_delete_rule_nullify_on_abstract_document(self): + """Ensure nullification of references to deleted documents when reference is on an abstract document. """ class AbstractBlogPost(Document): @@ -1474,7 +1474,7 @@ class QuerySetTest(unittest.TestCase): self.assertEqual(1, BlogPost.objects.count()) self.assertRaises(OperationError, self.Person.objects.delete) - + def test_reverse_delete_rule_pull(self): """Ensure pulling of references to deleted documents. """ @@ -1511,9 +1511,9 @@ class QuerySetTest(unittest.TestCase): """ class AbstractBlogPost(Document): meta = {'abstract': True} - authors = ListField(ReferenceField(self.Person, + authors = ListField(ReferenceField(self.Person, reverse_delete_rule=PULL)) - + class BlogPost(AbstractBlogPost): content = StringField() @@ -1538,7 +1538,7 @@ class QuerySetTest(unittest.TestCase): self.assertEqual(post.authors, [me]) self.assertEqual(another.authors, []) - + def test_delete_with_limits(self): class Log(Document): @@ -3009,7 +3009,7 @@ class QuerySetTest(unittest.TestCase): def test_distinct_ListField_EmbeddedDocumentField_EmbeddedDocumentField(self): class Continent(EmbeddedDocument): continent_name = StringField() - + class Country(EmbeddedDocument): country_name = StringField() continent = EmbeddedDocumentField(Continent) @@ -3026,7 +3026,7 @@ class QuerySetTest(unittest.TestCase): europe = Continent(continent_name='europe') asia = Continent(continent_name='asia') - + scotland = Country(country_name="Scotland", continent=europe) tibet = Country(country_name="Tibet", continent=asia) @@ -3041,9 +3041,9 @@ 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, [europe, asia]) def test_distinct_ListField_ReferenceField(self): @@ -4592,6 +4592,19 @@ class QuerySetTest(unittest.TestCase): self.assertEquals(Animal.objects(folded_ears=True).count(), 1) self.assertEquals(Animal.objects(whiskers_length=5.1).count(), 1) + def test_last_field_name_like_operator(self): + class EmbeddedItem(EmbeddedDocument): + type = StringField() + + class Doc(Document): + item = EmbeddedDocumentField(EmbeddedItem) + + Doc.drop_collection() + + doc = Doc(item=EmbeddedItem(type="axe")) + doc.save() + + self.assertEqual(1, Doc.objects(item__type__="axe").count()) if __name__ == '__main__': unittest.main() From dfa8eaf24e5fd2de6673807a8b6959f256270aa1 Mon Sep 17 00:00:00 2001 From: Eremeev Danil Date: Tue, 5 May 2015 12:00:46 +0500 Subject: [PATCH 2/3] Added changeset, updated documentation and tests, changed test condition --- docs/changelog.rst | 1 + docs/guide/querying.rst | 9 ++++++++- mongoengine/queryset/transform.py | 3 ++- tests/queryset/queryset.py | 4 +++- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index a27db5a4..53676562 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -16,6 +16,7 @@ Changes in 0.9.X - DEV - Django support was removed and will be available as a separate extension. #958 - Don't send a "cls" option to ensureIndex (related to https://jira.mongodb.org/browse/SERVER-769) - Fix for updating sorting in SortedListField. #978 +- Added __ support to escape field name in fields lookup keywords that match operators names #949 Changes in 0.9.0 ================ diff --git a/docs/guide/querying.rst b/docs/guide/querying.rst index 9d1dfd76..0a035469 100644 --- a/docs/guide/querying.rst +++ b/docs/guide/querying.rst @@ -39,6 +39,14 @@ syntax:: # been written by a user whose 'country' field is set to 'uk' uk_pages = Page.objects(author__country='uk') +.. note:: + + (version **0.9.0+**) if your field name is like mongodb operator name (for example + type, lte, lt...) and you want to place it at the end of lookup keyword + mongoengine automatically prepend $ to it. To avoid this use __ at the end of + your lookup keyword. For example if you field name is ``type`` and you want to + query by this field you must use ``.objects(user__type__="admin")`` instead of + ``.objects(user__type="admin")`` Query operators =============== @@ -663,4 +671,3 @@ following example shows how the substitutions are made:: return comments; } """) - diff --git a/mongoengine/queryset/transform.py b/mongoengine/queryset/transform.py index b08d450a..8ca3203c 100644 --- a/mongoengine/queryset/transform.py +++ b/mongoengine/queryset/transform.py @@ -44,7 +44,8 @@ def query(_doc_cls=None, _field_operation=False, **query): if len(parts) > 1 and parts[-1] in MATCH_OPERATORS: op = parts.pop() - if len(parts) > 1 and not parts[-1]: + #if user escape field name by __ + if len(parts) > 1 and parts[-1]=="": parts.pop() negate = False diff --git a/tests/queryset/queryset.py b/tests/queryset/queryset.py index 5d21cb0c..30c59bfa 100644 --- a/tests/queryset/queryset.py +++ b/tests/queryset/queryset.py @@ -4595,16 +4595,18 @@ class QuerySetTest(unittest.TestCase): def test_last_field_name_like_operator(self): class EmbeddedItem(EmbeddedDocument): type = StringField() + name = StringField() class Doc(Document): item = EmbeddedDocumentField(EmbeddedItem) Doc.drop_collection() - doc = Doc(item=EmbeddedItem(type="axe")) + doc = Doc(item=EmbeddedItem(type="axe", name="Heroic axe")) doc.save() self.assertEqual(1, Doc.objects(item__type__="axe").count()) + self.assertEqual(1, Doc.objects(item__name__="Heroic axe").count()) if __name__ == '__main__': unittest.main() From aab0599280be45e202191ec668db1beba31a79a1 Mon Sep 17 00:00:00 2001 From: Eremeev Danil Date: Thu, 7 May 2015 10:53:25 +0500 Subject: [PATCH 3/3] test moved to another file, cosmetical fixes --- docs/guide/querying.rst | 4 ++-- mongoengine/queryset/transform.py | 2 +- tests/queryset/queryset.py | 15 --------------- tests/queryset/transform.py | 16 ++++++++++++++++ 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/docs/guide/querying.rst b/docs/guide/querying.rst index 0a035469..9861ce56 100644 --- a/docs/guide/querying.rst +++ b/docs/guide/querying.rst @@ -41,10 +41,10 @@ syntax:: .. note:: - (version **0.9.0+**) if your field name is like mongodb operator name (for example + (version **0.9.1+**) if your field name is like mongodb operator name (for example type, lte, lt...) and you want to place it at the end of lookup keyword mongoengine automatically prepend $ to it. To avoid this use __ at the end of - your lookup keyword. For example if you field name is ``type`` and you want to + your lookup keyword. For example if your field name is ``type`` and you want to query by this field you must use ``.objects(user__type__="admin")`` instead of ``.objects(user__type="admin")`` diff --git a/mongoengine/queryset/transform.py b/mongoengine/queryset/transform.py index 8ca3203c..68adefbc 100644 --- a/mongoengine/queryset/transform.py +++ b/mongoengine/queryset/transform.py @@ -45,7 +45,7 @@ def query(_doc_cls=None, _field_operation=False, **query): op = parts.pop() #if user escape field name by __ - if len(parts) > 1 and parts[-1]=="": + if len(parts) > 1 and parts[-1] == "": parts.pop() negate = False diff --git a/tests/queryset/queryset.py b/tests/queryset/queryset.py index 30c59bfa..f407c0b7 100644 --- a/tests/queryset/queryset.py +++ b/tests/queryset/queryset.py @@ -4592,21 +4592,6 @@ class QuerySetTest(unittest.TestCase): self.assertEquals(Animal.objects(folded_ears=True).count(), 1) self.assertEquals(Animal.objects(whiskers_length=5.1).count(), 1) - def test_last_field_name_like_operator(self): - class EmbeddedItem(EmbeddedDocument): - type = StringField() - name = StringField() - - class Doc(Document): - item = EmbeddedDocumentField(EmbeddedItem) - - Doc.drop_collection() - - doc = Doc(item=EmbeddedItem(type="axe", name="Heroic axe")) - doc.save() - - self.assertEqual(1, Doc.objects(item__type__="axe").count()) - self.assertEqual(1, Doc.objects(item__name__="Heroic axe").count()) if __name__ == '__main__': unittest.main() diff --git a/tests/queryset/transform.py b/tests/queryset/transform.py index 2d5261f2..77d3593c 100644 --- a/tests/queryset/transform.py +++ b/tests/queryset/transform.py @@ -208,6 +208,22 @@ class TransformTest(unittest.TestCase): self.assertEqual(Doc.objects(df__type=2).count(), 1) # str self.assertEqual(Doc.objects(df__type=16).count(), 1) # int + def test_last_field_name_like_operator(self): + class EmbeddedItem(EmbeddedDocument): + type = StringField() + name = StringField() + + class Doc(Document): + item = EmbeddedDocumentField(EmbeddedItem) + + Doc.drop_collection() + + doc = Doc(item=EmbeddedItem(type="axe", name="Heroic axe")) + doc.save() + + self.assertEqual(1, Doc.objects(item__type__="axe").count()) + self.assertEqual(1, Doc.objects(item__name__="Heroic axe").count()) + if __name__ == '__main__': unittest.main()