Skip to content

Commit d6b2d8d

Browse files
committed
Merge pull request #982 from elephanter/operator_name_in_field_name
Added __ support to escape field name in fields lookup keywords that match operators names
2 parents c642eee + aab0599 commit d6b2d8d

File tree

5 files changed

+41
-13
lines changed

5 files changed

+41
-13
lines changed

docs/changelog.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Changes in 0.9.X - DEV
1616
- Django support was removed and will be available as a separate extension. #958
1717
- Don't send a "cls" option to ensureIndex (related to https://jira.mongodb.org/browse/SERVER-769)
1818
- Fix for updating sorting in SortedListField. #978
19+
- Added __ support to escape field name in fields lookup keywords that match operators names #949
1920
2021
Changes in 0.9.0
2122
================

docs/guide/querying.rst

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,14 @@ syntax::
3939
# been written by a user whose 'country' field is set to 'uk'
4040
uk_pages = Page.objects(author__country='uk')
4141

42+
.. note::
43+
44+
(version **0.9.1+**) if your field name is like mongodb operator name (for example
45+
type, lte, lt...) and you want to place it at the end of lookup keyword
46+
mongoengine automatically prepend $ to it. To avoid this use __ at the end of
47+
your lookup keyword. For example if your field name is ``type`` and you want to
48+
query by this field you must use ``.objects(user__type__="admin")`` instead of
49+
``.objects(user__type="admin")``
4250

4351
Query operators
4452
===============
@@ -663,4 +671,3 @@ following example shows how the substitutions are made::
663671
return comments;
664672
}
665673
""")
666-

mongoengine/queryset/transform.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ def query(_doc_cls=None, _field_operation=False, **query):
4444
if len(parts) > 1 and parts[-1] in MATCH_OPERATORS:
4545
op = parts.pop()
4646

47+
#if user escape field name by __
48+
if len(parts) > 1 and parts[-1] == "":
49+
parts.pop()
50+
4751
negate = False
4852
if len(parts) > 1 and parts[-1] == 'not':
4953
parts.pop()

tests/queryset/queryset.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1328,7 +1328,7 @@ class BlogPost(Document):
13281328
self.assertEqual(1, BlogPost.objects.count())
13291329

13301330
def test_reverse_delete_rule_cascade_on_abstract_document(self):
1331-
"""Ensure cascading deletion of referring documents from the database
1331+
"""Ensure cascading deletion of referring documents from the database
13321332
does not fail on abstract document.
13331333
"""
13341334
class AbstractBlogPost(Document):
@@ -1350,7 +1350,7 @@ class BlogPost(AbstractBlogPost):
13501350

13511351
self.assertEqual(3, BlogPost.objects.count())
13521352
self.Person.objects(name='Test User').delete()
1353-
self.assertEqual(1, BlogPost.objects.count())
1353+
self.assertEqual(1, BlogPost.objects.count())
13541354

13551355
def test_reverse_delete_rule_cascade_self_referencing(self):
13561356
"""Ensure self-referencing CASCADE deletes do not result in infinite
@@ -1411,8 +1411,8 @@ class BlogPost(Document):
14111411
self.assertEqual(1, BlogPost.objects.count())
14121412
self.assertEqual(None, BlogPost.objects.first().category)
14131413

1414-
def test_reverse_delete_rule_nullify_on_abstract_document(self):
1415-
"""Ensure nullification of references to deleted documents when
1414+
def test_reverse_delete_rule_nullify_on_abstract_document(self):
1415+
"""Ensure nullification of references to deleted documents when
14161416
reference is on an abstract document.
14171417
"""
14181418
class AbstractBlogPost(Document):
@@ -1474,7 +1474,7 @@ class BlogPost(AbstractBlogPost):
14741474

14751475
self.assertEqual(1, BlogPost.objects.count())
14761476
self.assertRaises(OperationError, self.Person.objects.delete)
1477-
1477+
14781478
def test_reverse_delete_rule_pull(self):
14791479
"""Ensure pulling of references to deleted documents.
14801480
"""
@@ -1511,9 +1511,9 @@ def test_reverse_delete_rule_pull_on_abstract_documents(self):
15111511
"""
15121512
class AbstractBlogPost(Document):
15131513
meta = {'abstract': True}
1514-
authors = ListField(ReferenceField(self.Person,
1514+
authors = ListField(ReferenceField(self.Person,
15151515
reverse_delete_rule=PULL))
1516-
1516+
15171517
class BlogPost(AbstractBlogPost):
15181518
content = StringField()
15191519

@@ -1538,7 +1538,7 @@ class BlogPost(AbstractBlogPost):
15381538

15391539
self.assertEqual(post.authors, [me])
15401540
self.assertEqual(another.authors, [])
1541-
1541+
15421542
def test_delete_with_limits(self):
15431543

15441544
class Log(Document):
@@ -3009,7 +3009,7 @@ class Book(Document):
30093009
def test_distinct_ListField_EmbeddedDocumentField_EmbeddedDocumentField(self):
30103010
class Continent(EmbeddedDocument):
30113011
continent_name = StringField()
3012-
3012+
30133013
class Country(EmbeddedDocument):
30143014
country_name = StringField()
30153015
continent = EmbeddedDocumentField(Continent)
@@ -3026,7 +3026,7 @@ class Book(Document):
30263026

30273027
europe = Continent(continent_name='europe')
30283028
asia = Continent(continent_name='asia')
3029-
3029+
30303030
scotland = Country(country_name="Scotland", continent=europe)
30313031
tibet = Country(country_name="Tibet", continent=asia)
30323032

@@ -3041,9 +3041,9 @@ class Book(Document):
30413041
country_list = Book.objects.distinct("authors.country")
30423042

30433043
self.assertEqual(country_list, [scotland, tibet])
3044-
3044+
30453045
continent_list = Book.objects.distinct("authors.country.continent")
3046-
3046+
30473047
self.assertEqual(continent_list, [europe, asia])
30483048

30493049
def test_distinct_ListField_ReferenceField(self):

tests/queryset/transform.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,22 @@ class Doc(Document):
208208
self.assertEqual(Doc.objects(df__type=2).count(), 1) # str
209209
self.assertEqual(Doc.objects(df__type=16).count(), 1) # int
210210

211+
def test_last_field_name_like_operator(self):
212+
class EmbeddedItem(EmbeddedDocument):
213+
type = StringField()
214+
name = StringField()
215+
216+
class Doc(Document):
217+
item = EmbeddedDocumentField(EmbeddedItem)
218+
219+
Doc.drop_collection()
220+
221+
doc = Doc(item=EmbeddedItem(type="axe", name="Heroic axe"))
222+
doc.save()
223+
224+
self.assertEqual(1, Doc.objects(item__type__="axe").count())
225+
self.assertEqual(1, Doc.objects(item__name__="Heroic axe").count())
226+
211227

212228
if __name__ == '__main__':
213229
unittest.main()

0 commit comments

Comments
 (0)