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
This commit is contained in:
Matthieu Rigal 2015-05-07 11:47:12 +02:00
commit d6b2d8dcb5
5 changed files with 41 additions and 13 deletions

View File

@ -16,6 +16,7 @@ Changes in 0.9.X - DEV
- Django support was removed and will be available as a separate extension. #958 - 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) - Don't send a "cls" option to ensureIndex (related to https://jira.mongodb.org/browse/SERVER-769)
- Fix for updating sorting in SortedListField. #978 - 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 Changes in 0.9.0
================ ================

View File

@ -39,6 +39,14 @@ syntax::
# been written by a user whose 'country' field is set to 'uk' # been written by a user whose 'country' field is set to 'uk'
uk_pages = Page.objects(author__country='uk') uk_pages = Page.objects(author__country='uk')
.. note::
(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 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")``
Query operators Query operators
=============== ===============
@ -663,4 +671,3 @@ following example shows how the substitutions are made::
return comments; return comments;
} }
""") """)

View File

@ -44,6 +44,10 @@ def query(_doc_cls=None, _field_operation=False, **query):
if len(parts) > 1 and parts[-1] in MATCH_OPERATORS: if len(parts) > 1 and parts[-1] in MATCH_OPERATORS:
op = parts.pop() op = parts.pop()
#if user escape field name by __
if len(parts) > 1 and parts[-1] == "":
parts.pop()
negate = False negate = False
if len(parts) > 1 and parts[-1] == 'not': if len(parts) > 1 and parts[-1] == 'not':
parts.pop() parts.pop()

View File

@ -1328,7 +1328,7 @@ class QuerySetTest(unittest.TestCase):
self.assertEqual(1, BlogPost.objects.count()) self.assertEqual(1, BlogPost.objects.count())
def test_reverse_delete_rule_cascade_on_abstract_document(self): 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. does not fail on abstract document.
""" """
class AbstractBlogPost(Document): class AbstractBlogPost(Document):
@ -1350,7 +1350,7 @@ class QuerySetTest(unittest.TestCase):
self.assertEqual(3, BlogPost.objects.count()) self.assertEqual(3, BlogPost.objects.count())
self.Person.objects(name='Test User').delete() 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): def test_reverse_delete_rule_cascade_self_referencing(self):
"""Ensure self-referencing CASCADE deletes do not result in infinite """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(1, BlogPost.objects.count())
self.assertEqual(None, BlogPost.objects.first().category) self.assertEqual(None, BlogPost.objects.first().category)
def test_reverse_delete_rule_nullify_on_abstract_document(self): def test_reverse_delete_rule_nullify_on_abstract_document(self):
"""Ensure nullification of references to deleted documents when """Ensure nullification of references to deleted documents when
reference is on an abstract document. reference is on an abstract document.
""" """
class AbstractBlogPost(Document): class AbstractBlogPost(Document):
@ -1474,7 +1474,7 @@ class QuerySetTest(unittest.TestCase):
self.assertEqual(1, BlogPost.objects.count()) self.assertEqual(1, BlogPost.objects.count())
self.assertRaises(OperationError, self.Person.objects.delete) self.assertRaises(OperationError, self.Person.objects.delete)
def test_reverse_delete_rule_pull(self): def test_reverse_delete_rule_pull(self):
"""Ensure pulling of references to deleted documents. """Ensure pulling of references to deleted documents.
""" """
@ -1511,9 +1511,9 @@ class QuerySetTest(unittest.TestCase):
""" """
class AbstractBlogPost(Document): class AbstractBlogPost(Document):
meta = {'abstract': True} meta = {'abstract': True}
authors = ListField(ReferenceField(self.Person, authors = ListField(ReferenceField(self.Person,
reverse_delete_rule=PULL)) reverse_delete_rule=PULL))
class BlogPost(AbstractBlogPost): class BlogPost(AbstractBlogPost):
content = StringField() content = StringField()
@ -1538,7 +1538,7 @@ class QuerySetTest(unittest.TestCase):
self.assertEqual(post.authors, [me]) self.assertEqual(post.authors, [me])
self.assertEqual(another.authors, []) self.assertEqual(another.authors, [])
def test_delete_with_limits(self): def test_delete_with_limits(self):
class Log(Document): class Log(Document):
@ -3009,7 +3009,7 @@ class QuerySetTest(unittest.TestCase):
def test_distinct_ListField_EmbeddedDocumentField_EmbeddedDocumentField(self): def test_distinct_ListField_EmbeddedDocumentField_EmbeddedDocumentField(self):
class Continent(EmbeddedDocument): class Continent(EmbeddedDocument):
continent_name = StringField() continent_name = StringField()
class Country(EmbeddedDocument): class Country(EmbeddedDocument):
country_name = StringField() country_name = StringField()
continent = EmbeddedDocumentField(Continent) continent = EmbeddedDocumentField(Continent)
@ -3026,7 +3026,7 @@ class QuerySetTest(unittest.TestCase):
europe = Continent(continent_name='europe') europe = Continent(continent_name='europe')
asia = Continent(continent_name='asia') asia = Continent(continent_name='asia')
scotland = Country(country_name="Scotland", continent=europe) scotland = Country(country_name="Scotland", continent=europe)
tibet = Country(country_name="Tibet", continent=asia) tibet = Country(country_name="Tibet", continent=asia)
@ -3041,9 +3041,9 @@ class QuerySetTest(unittest.TestCase):
country_list = Book.objects.distinct("authors.country") country_list = Book.objects.distinct("authors.country")
self.assertEqual(country_list, [scotland, tibet]) self.assertEqual(country_list, [scotland, tibet])
continent_list = Book.objects.distinct("authors.country.continent") continent_list = Book.objects.distinct("authors.country.continent")
self.assertEqual(continent_list, [europe, asia]) self.assertEqual(continent_list, [europe, asia])
def test_distinct_ListField_ReferenceField(self): def test_distinct_ListField_ReferenceField(self):

View File

@ -208,6 +208,22 @@ class TransformTest(unittest.TestCase):
self.assertEqual(Doc.objects(df__type=2).count(), 1) # str self.assertEqual(Doc.objects(df__type=2).count(), 1) # str
self.assertEqual(Doc.objects(df__type=16).count(), 1) # int 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__': if __name__ == '__main__':
unittest.main() unittest.main()