From 6903eed4e770bcb605df948728121ce75149d0fa Mon Sep 17 00:00:00 2001 From: Erdenezul Batmunkh Date: Thu, 15 Jun 2017 06:08:40 +0000 Subject: [PATCH 01/12] support position in 'push' #1565 --- mongoengine/queryset/transform.py | 16 +++++++++++++--- tests/queryset/queryset.py | 23 +++++++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/mongoengine/queryset/transform.py b/mongoengine/queryset/transform.py index bb04ee37..2c1b7fdc 100644 --- a/mongoengine/queryset/transform.py +++ b/mongoengine/queryset/transform.py @@ -284,9 +284,11 @@ def update(_doc_cls=None, **update): if isinstance(field, GeoJsonBaseField): value = field.to_mongo(value) - if op in (None, 'set', 'push', 'pull'): + if op == 'push' and isinstance(value, (list, tuple, set)): + value = [field.prepare_query_value(op, v) for v in value] + elif op in (None, 'set', 'push', 'pull'): if field.required or value is not None: - value = field.prepare_query_value(op, value) + value = field.prepare_query_value(op, value) elif op in ('pushAll', 'pullAll'): value = [field.prepare_query_value(op, v) for v in value] elif op in ('addToSet', 'setOnInsert'): @@ -302,6 +304,10 @@ def update(_doc_cls=None, **update): value = {match: value} key = '.'.join(parts) + position = None + if parts[-1].isdigit() and isinstance(value, (list, tuple, set)): + key = parts[0] + position = int(parts[-1]) if not op: raise InvalidQueryError('Updates must supply an operation ' @@ -333,10 +339,14 @@ def update(_doc_cls=None, **update): value = {key: value} elif op == 'addToSet' and isinstance(value, list): value = {key: {'$each': value}} + elif op == 'push' and isinstance(value, list): + if position is not None: + value = {key: {'$each': value, '$position': position}} + else: + value = {key: {'$each': value}} else: value = {key: value} key = '$' + op - if key not in mongo_update: mongo_update[key] = value elif key in mongo_update and isinstance(mongo_update[key], dict): diff --git a/tests/queryset/queryset.py b/tests/queryset/queryset.py index d97b307d..9341a214 100644 --- a/tests/queryset/queryset.py +++ b/tests/queryset/queryset.py @@ -1903,6 +1903,29 @@ class QuerySetTest(unittest.TestCase): BlogPost.drop_collection() + def test_update_push_with_position(self): + """Ensure that the 'push' update with position works properly. + """ + class BlogPost(Document): + slug = StringField() + tags = ListField(StringField()) + + BlogPost.drop_collection() + + post = BlogPost(slug="test") + post.save() + + BlogPost.objects.filter(id=post.id).update(push__tags="code") + BlogPost.objects.filter(id=post.id).update(push__tags__0=["mongodb", "python"]) + post.reload() + self.assertEqual(post.tags[0], "mongodb") + self.assertEqual(post.tags[1], "python") + self.assertEqual(post.tags[2], "code") + + BlogPost.objects.filter(id=post.id).update(set__tags__2="java") + post.reload() + self.assertEqual(post.tags[2], "java") + def test_update_push_and_pull_add_to_set(self): """Ensure that the 'pull' update operation works correctly. """ From f63ad2dd69c40d87962fe05137e618c09418ed5e Mon Sep 17 00:00:00 2001 From: Erdenezul Batmunkh Date: Thu, 15 Jun 2017 07:36:14 +0000 Subject: [PATCH 02/12] dont test in mongoDB v2.4 #1565 --- tests/queryset/queryset.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/queryset/queryset.py b/tests/queryset/queryset.py index 9341a214..7be3f8d7 100644 --- a/tests/queryset/queryset.py +++ b/tests/queryset/queryset.py @@ -1903,6 +1903,7 @@ class QuerySetTest(unittest.TestCase): BlogPost.drop_collection() + @needs_mongodb_v26 def test_update_push_with_position(self): """Ensure that the 'push' update with position works properly. """ From 1d4b1870cfb50e3b91b0fe187874c31d5373ea65 Mon Sep 17 00:00:00 2001 From: Danil Date: Mon, 19 Jun 2017 05:04:46 +0500 Subject: [PATCH 03/12] to_db_fields fix (#1553) --- mongoengine/queryset/base.py | 4 ++-- tests/queryset/field_list.py | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/mongoengine/queryset/base.py b/mongoengine/queryset/base.py index f7c32d20..41a10ace 100644 --- a/mongoengine/queryset/base.py +++ b/mongoengine/queryset/base.py @@ -1731,14 +1731,14 @@ class BaseQuerySet(object): for x in document._subclasses][1:] for field in fields: try: - field = '.'.join(f.db_field for f in + field = '.'.join(f if isinstance(f, six.string_types) else f.db_field for f in document._lookup_field(field.split('.'))) ret.append(field) except LookUpError as err: found = False for subdoc in subclasses: try: - subfield = '.'.join(f.db_field for f in + subfield = '.'.join(f if isinstance(f, six.string_types) else f.db_field for f in subdoc._lookup_field(field.split('.'))) ret.append(subfield) found = True diff --git a/tests/queryset/field_list.py b/tests/queryset/field_list.py index d1277e06..c07cec3e 100644 --- a/tests/queryset/field_list.py +++ b/tests/queryset/field_list.py @@ -197,14 +197,18 @@ class OnlyExcludeAllTest(unittest.TestCase): title = StringField() text = StringField() + class VariousData(EmbeddedDocument): + some = BooleanField() + class BlogPost(Document): content = StringField() author = EmbeddedDocumentField(User) comments = ListField(EmbeddedDocumentField(Comment)) + various = MapField(field=EmbeddedDocumentField(VariousData)) BlogPost.drop_collection() - post = BlogPost(content='Had a good coffee today...') + post = BlogPost(content='Had a good coffee today...', various={'test_dynamic':{'some': True}}) post.author = User(name='Test User') post.comments = [Comment(title='I aggree', text='Great post!'), Comment(title='Coffee', text='I hate coffee')] post.save() @@ -215,6 +219,9 @@ class OnlyExcludeAllTest(unittest.TestCase): self.assertEqual(obj.author.name, 'Test User') self.assertEqual(obj.comments, []) + obj = BlogPost.objects.only('various.test_dynamic.some').get() + self.assertEqual(obj.various["test_dynamic"].some, True) + obj = BlogPost.objects.only('content', 'comments.title',).get() self.assertEqual(obj.content, 'Had a good coffee today...') self.assertEqual(obj.author, None) From a8d6e59a7a0434edebcb52ca63e40be5466548fa Mon Sep 17 00:00:00 2001 From: Stefan Wojcik Date: Sun, 18 Jun 2017 17:25:39 -0700 Subject: [PATCH 04/12] minor tweaks to code quality in _fields_to_dbfields --- mongoengine/queryset/base.py | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/mongoengine/queryset/base.py b/mongoengine/queryset/base.py index 41a10ace..6f9c372c 100644 --- a/mongoengine/queryset/base.py +++ b/mongoengine/queryset/base.py @@ -1722,25 +1722,33 @@ class BaseQuerySet(object): return frequencies def _fields_to_dbfields(self, fields): - """Translate fields paths to its db equivalents""" - ret = [] + """Translate fields' paths to their db equivalents.""" subclasses = [] - document = self._document - if document._meta['allow_inheritance']: + if self._document._meta['allow_inheritance']: subclasses = [get_document(x) - for x in document._subclasses][1:] + for x in self._document._subclasses][1:] + + db_field_paths = [] for field in fields: + field_parts = field.split('.') try: - field = '.'.join(f if isinstance(f, six.string_types) else f.db_field for f in - document._lookup_field(field.split('.'))) - ret.append(field) + field = '.'.join( + f if isinstance(f, six.string_types) else f.db_field + for f in self._document._lookup_field(field_parts) + ) + db_field_paths.append(field) except LookUpError as err: found = False + + # If a field path wasn't found on the main document, go + # through its subclasses and see if it exists on any of them. for subdoc in subclasses: try: - subfield = '.'.join(f if isinstance(f, six.string_types) else f.db_field for f in - subdoc._lookup_field(field.split('.'))) - ret.append(subfield) + subfield = '.'.join( + f if isinstance(f, six.string_types) else f.db_field + for f in subdoc._lookup_field(field_parts) + ) + db_field_paths.append(subfield) found = True break except LookUpError: @@ -1748,7 +1756,8 @@ class BaseQuerySet(object): if not found: raise err - return ret + + return db_field_paths def _get_order_by(self, keys): """Given a list of MongoEngine-style sort keys, return a list From f3ee4a5dac9a6bcccdd11f9be3afc702e344e8a2 Mon Sep 17 00:00:00 2001 From: Erdenezul Batmunkh Date: Mon, 19 Jun 2017 02:59:17 +0000 Subject: [PATCH 05/12] add tests for push operator #1565 --- AUTHORS | 1 + tests/document/instance.py | 41 ++++++++++++++++++++++++++++++++++++++ tests/queryset/modify.py | 25 ++++++++++++++++++++++- 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index 1d724718..88d4bbe1 100644 --- a/AUTHORS +++ b/AUTHORS @@ -243,3 +243,4 @@ that much better: * Victor Varvaryuk * Stanislav Kaledin (https://github.com/sallyruthstruik) * Dmitry Yantsen (https://github.com/mrTable) + * Erdenezul Batmunkh (https://github.com/erdenezul) diff --git a/tests/document/instance.py b/tests/document/instance.py index 37bbe337..1456f0a1 100644 --- a/tests/document/instance.py +++ b/tests/document/instance.py @@ -22,6 +22,8 @@ from mongoengine.queryset import NULLIFY, Q from mongoengine.context_managers import switch_db, query_counter from mongoengine import signals +from tests.utils import needs_mongodb_v26 + TEST_IMAGE_PATH = os.path.join(os.path.dirname(__file__), '../fields/mongoengine.png') @@ -775,6 +777,7 @@ class InstanceTest(unittest.TestCase): self.assertDbEqual([dict(doc.to_mongo())]) + def test_modify_invalid_query(self): doc1 = self.Person(name="bob", age=10).save() doc2 = self.Person(name="jim", age=20).save() @@ -826,6 +829,27 @@ class InstanceTest(unittest.TestCase): self.assertDbEqual([dict(other_doc.to_mongo()), dict(doc.to_mongo())]) + @needs_mongodb_v26 + def test_modity_push_position(self): + class BlogPost(Document): + slug = StringField() + tags = ListField(StringField()) + + other_blog = BlogPost(slug="ABC", tags=["code", "java", "python"]).save() + + blog = BlogPost(slug="ABC", tags=["python"]).save() + blog_copy = blog._from_son(blog.to_mongo()) + + assert blog.modify(push__tags__0=["code", "java"]) + blog_copy.tags = ["code", "java", "python"] + assert blog.to_json() == blog_copy.to_json() + assert blog._get_changed_fields() == [] + + docs = [dict(other_blog.to_mongo()), dict(blog.to_mongo())] + self.assertEqual( + list(BlogPost._get_collection().find().sort("id")), + sorted(docs, key=lambda doc: doc["_id"])) + def test_save(self): """Ensure that a document may be saved in the database.""" @@ -3149,6 +3173,23 @@ class InstanceTest(unittest.TestCase): person.update(set__height=2.0) + @needs_mongodb_v26 + def test_push_with_position(self): + """Ensure that push with position works properly for an instance.""" + class BlogPost(Document): + slug = StringField() + tags = ListField(StringField()) + + blog = BlogPost() + blog.slug = "ABC" + blog.tags = ["python"] + blog.save() + + blog.update(push__tags__0=["mongodb", "code"]) + blog.reload() + self.assertEqual(blog.tags[0], "mongodb") + self.assertEqual(blog.tags[2], "python") + if __name__ == '__main__': unittest.main() diff --git a/tests/queryset/modify.py b/tests/queryset/modify.py index 607937f6..4bda9718 100644 --- a/tests/queryset/modify.py +++ b/tests/queryset/modify.py @@ -1,6 +1,6 @@ import unittest -from mongoengine import connect, Document, IntField +from mongoengine import connect, Document, IntField, StringField, ListField __all__ = ("FindAndModifyTest",) @@ -94,6 +94,29 @@ class FindAndModifyTest(unittest.TestCase): self.assertEqual(old_doc.to_mongo(), {"_id": 1}) self.assertDbEqual([{"_id": 0, "value": 0}, {"_id": 1, "value": -1}]) + def test_modify_with_push(self): + class BlogPost(Document): + id = StringField(primary_key=True) + tags = ListField(StringField()) + + BlogPost.drop_collection() + + BlogPost(id="ABC").save() + BlogPost(id="BCD").save() + blog = BlogPost.objects(id="ABC").modify(push__tags="code") + + self.assertEqual(blog.to_mongo(), {"_id": "ABC", "tags": []}) + docs = [{"_id": "ABC", "tags":["code"]}, {"_id": "BCD", "tags":[]}] + self.assertEqual(list(BlogPost._collection.find().sort("id")), docs) + + another_blog = BlogPost.objects(id="BCD").modify(push__tags="java") + self.assertEqual(another_blog.to_mongo(), {"_id": "BCD", "tags": []}) + another_blog = BlogPost.objects(id="BCD").modify(push__tags__0=["python"]) + self.assertEqual(another_blog.to_mongo(), {"_id": "BCD", "tags": ["java"]}) + docs = [{"_id": "ABC", "tags":["code"]}, + {"_id": "BCD", "tags":["python", "java"]}] + self.assertEqual(list(BlogPost._collection.find().sort("id")), docs) + if __name__ == '__main__': unittest.main() From 7782aa7379306c8223edd33aeec6b47f2f145b83 Mon Sep 17 00:00:00 2001 From: Erdenezul Batmunkh Date: Mon, 19 Jun 2017 03:11:59 +0000 Subject: [PATCH 06/12] do not test position push in mongodb_v2.4 #1565 --- tests/document/instance.py | 1 - tests/queryset/modify.py | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/document/instance.py b/tests/document/instance.py index 1456f0a1..26ab0d97 100644 --- a/tests/document/instance.py +++ b/tests/document/instance.py @@ -777,7 +777,6 @@ class InstanceTest(unittest.TestCase): self.assertDbEqual([dict(doc.to_mongo())]) - def test_modify_invalid_query(self): doc1 = self.Person(name="bob", age=10).save() doc2 = self.Person(name="jim", age=20).save() diff --git a/tests/queryset/modify.py b/tests/queryset/modify.py index 4bda9718..44bdc3ff 100644 --- a/tests/queryset/modify.py +++ b/tests/queryset/modify.py @@ -2,6 +2,8 @@ import unittest from mongoengine import connect, Document, IntField, StringField, ListField +from tests.utils import needs_mongodb_v26 + __all__ = ("FindAndModifyTest",) @@ -94,6 +96,7 @@ class FindAndModifyTest(unittest.TestCase): self.assertEqual(old_doc.to_mongo(), {"_id": 1}) self.assertDbEqual([{"_id": 0, "value": 0}, {"_id": 1, "value": -1}]) + @needs_mongodb_v26 def test_modify_with_push(self): class BlogPost(Document): id = StringField(primary_key=True) From fb00b79d1945a05c08f642aa35efbba5e92558a8 Mon Sep 17 00:00:00 2001 From: Erdenezul Batmunkh Date: Mon, 19 Jun 2017 03:28:34 +0000 Subject: [PATCH 07/12] add docs for positional push operator #1565 --- docs/guide/querying.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/guide/querying.rst b/docs/guide/querying.rst index 0bb19658..f1594dd2 100644 --- a/docs/guide/querying.rst +++ b/docs/guide/querying.rst @@ -565,6 +565,15 @@ cannot use the `$` syntax in keyword arguments it has been mapped to `S`:: >>> post.tags ['database', 'mongodb'] +From MongoDB version 2.6, push operator supports $position value which allows +to push values with index. + >>> post = BlogPost(title="Test", tags=["mongo"]) + >>> post.save() + >>> post.update(push__tags__0=["database", "code"]) + >>> post.reload() + >>> post.tags + ['database', 'code', 'mongo'] + .. note:: Currently only top level lists are handled, future versions of mongodb / pymongo plan to support nested positional operators. See `The $ positional From a7cab513695ba1814d9316719429d63249fb97dc Mon Sep 17 00:00:00 2001 From: Davidrjx <1058960881@qq.com> Date: Thu, 13 Jul 2017 18:07:36 +0800 Subject: [PATCH 08/12] Use a set literal in _clean_settings (#1585) --- AUTHORS | 1 + mongoengine/connection.py | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/AUTHORS b/AUTHORS index 1d724718..96a7850e 100644 --- a/AUTHORS +++ b/AUTHORS @@ -243,3 +243,4 @@ that much better: * Victor Varvaryuk * Stanislav Kaledin (https://github.com/sallyruthstruik) * Dmitry Yantsen (https://github.com/mrTable) + * Renjianxin (https://github.com/Davidrjx) diff --git a/mongoengine/connection.py b/mongoengine/connection.py index 7eae810f..34ff4dc3 100644 --- a/mongoengine/connection.py +++ b/mongoengine/connection.py @@ -146,13 +146,14 @@ def get_connection(alias=DEFAULT_CONNECTION_NAME, reconnect=False): raise MongoEngineConnectionError(msg) def _clean_settings(settings_dict): - irrelevant_fields = set([ - 'name', 'username', 'password', 'authentication_source', - 'authentication_mechanism' - ]) + # set literal more efficient than calling set function + irrelevant_fields_set = { + 'name', 'username', 'password', + 'authentication_source', 'authentication_mechanism' + } return { k: v for k, v in settings_dict.items() - if k not in irrelevant_fields + if k not in irrelevant_fields_set } # Retrieve a copy of the connection settings associated with the requested From 3dcc9bc1433e550a3151ade5f31efef7e39f2be3 Mon Sep 17 00:00:00 2001 From: Erdenezul Batmunkh Date: Thu, 13 Jul 2017 22:59:21 +0800 Subject: [PATCH 09/12] use explicit tests and fix unneccessary indent #1565 --- mongoengine/queryset/transform.py | 2 +- tests/document/instance.py | 26 ++++++++++---------------- tests/queryset/queryset.py | 6 ++---- 3 files changed, 13 insertions(+), 21 deletions(-) diff --git a/mongoengine/queryset/transform.py b/mongoengine/queryset/transform.py index 2c1b7fdc..b9c8b130 100644 --- a/mongoengine/queryset/transform.py +++ b/mongoengine/queryset/transform.py @@ -288,7 +288,7 @@ def update(_doc_cls=None, **update): value = [field.prepare_query_value(op, v) for v in value] elif op in (None, 'set', 'push', 'pull'): if field.required or value is not None: - value = field.prepare_query_value(op, value) + value = field.prepare_query_value(op, value) elif op in ('pushAll', 'pullAll'): value = [field.prepare_query_value(op, v) for v in value] elif op in ('addToSet', 'setOnInsert'): diff --git a/tests/document/instance.py b/tests/document/instance.py index 26ab0d97..609bc900 100644 --- a/tests/document/instance.py +++ b/tests/document/instance.py @@ -829,25 +829,20 @@ class InstanceTest(unittest.TestCase): self.assertDbEqual([dict(other_doc.to_mongo()), dict(doc.to_mongo())]) @needs_mongodb_v26 - def test_modity_push_position(self): + def test_modify_with_positional_push(self): class BlogPost(Document): - slug = StringField() tags = ListField(StringField()) - other_blog = BlogPost(slug="ABC", tags=["code", "java", "python"]).save() + post = BlogPost.objects.create(tags=['python']) + self.assertEqual(post.tags, ['python']) + post.modify(push__tags__0=['code', 'mongo']) + self.assertEqual(post.tags, ['code', 'mongo', 'python']) - blog = BlogPost(slug="ABC", tags=["python"]).save() - blog_copy = blog._from_son(blog.to_mongo()) - - assert blog.modify(push__tags__0=["code", "java"]) - blog_copy.tags = ["code", "java", "python"] - assert blog.to_json() == blog_copy.to_json() - assert blog._get_changed_fields() == [] - - docs = [dict(other_blog.to_mongo()), dict(blog.to_mongo())] + # Assert same order of the list items is maintained in the db self.assertEqual( - list(BlogPost._get_collection().find().sort("id")), - sorted(docs, key=lambda doc: doc["_id"])) + BlogPost._get_collection().find_one({'_id': post.pk})['tags'], + ['code', 'mongo', 'python'] + ) def test_save(self): """Ensure that a document may be saved in the database.""" @@ -3186,8 +3181,7 @@ class InstanceTest(unittest.TestCase): blog.update(push__tags__0=["mongodb", "code"]) blog.reload() - self.assertEqual(blog.tags[0], "mongodb") - self.assertEqual(blog.tags[2], "python") + self.assertEqual(blog.tags, ['mongodb', 'code', 'python']) if __name__ == '__main__': diff --git a/tests/queryset/queryset.py b/tests/queryset/queryset.py index 7be3f8d7..f4e4670d 100644 --- a/tests/queryset/queryset.py +++ b/tests/queryset/queryset.py @@ -1919,13 +1919,11 @@ class QuerySetTest(unittest.TestCase): BlogPost.objects.filter(id=post.id).update(push__tags="code") BlogPost.objects.filter(id=post.id).update(push__tags__0=["mongodb", "python"]) post.reload() - self.assertEqual(post.tags[0], "mongodb") - self.assertEqual(post.tags[1], "python") - self.assertEqual(post.tags[2], "code") + self.assertEqual(post.tags, ['mongodb', 'python', 'code']) BlogPost.objects.filter(id=post.id).update(set__tags__2="java") post.reload() - self.assertEqual(post.tags[2], "java") + self.assertEqual(post.tags, ['mongodb', 'python', 'java']) def test_update_push_and_pull_add_to_set(self): """Ensure that the 'pull' update operation works correctly. From 433f10ef93cbfa2d2f0a8cb704e8333704ebf362 Mon Sep 17 00:00:00 2001 From: Erdenezul Batmunkh Date: Mon, 31 Jul 2017 05:15:23 +0000 Subject: [PATCH 10/12] position support singular value #1565 --- mongoengine/queryset/transform.py | 17 ++++++++++------- tests/queryset/queryset.py | 5 +++++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/mongoengine/queryset/transform.py b/mongoengine/queryset/transform.py index b9c8b130..b01d3d41 100644 --- a/mongoengine/queryset/transform.py +++ b/mongoengine/queryset/transform.py @@ -304,10 +304,6 @@ def update(_doc_cls=None, **update): value = {match: value} key = '.'.join(parts) - position = None - if parts[-1].isdigit() and isinstance(value, (list, tuple, set)): - key = parts[0] - position = int(parts[-1]) if not op: raise InvalidQueryError('Updates must supply an operation ' @@ -339,11 +335,18 @@ def update(_doc_cls=None, **update): value = {key: value} elif op == 'addToSet' and isinstance(value, list): value = {key: {'$each': value}} - elif op == 'push' and isinstance(value, list): - if position is not None: + elif op == 'push': + if parts[-1].isdigit() and op == 'push': + key = parts[0] + position = int(parts[-1]) + # position modifier must appear with each. + if not isinstance(value, (set, tuple, list)): + value = [value] value = {key: {'$each': value, '$position': position}} - else: + elif isinstance(value, list): value = {key: {'$each': value}} + else: + value = {key: value} else: value = {key: value} key = '$' + op diff --git a/tests/queryset/queryset.py b/tests/queryset/queryset.py index f4e4670d..fe97b765 100644 --- a/tests/queryset/queryset.py +++ b/tests/queryset/queryset.py @@ -1925,6 +1925,11 @@ class QuerySetTest(unittest.TestCase): post.reload() self.assertEqual(post.tags, ['mongodb', 'python', 'java']) + #test push with singular value + BlogPost.objects.filter(id=post.id).update(push__tags__0='scala') + post.reload() + self.assertEqual(post.tags, ['scala', 'mongodb', 'python', 'java']) + def test_update_push_and_pull_add_to_set(self): """Ensure that the 'pull' update operation works correctly. """ From 34fca9d6f5cb8ea09af7dc63a480354c8ecbc06d Mon Sep 17 00:00:00 2001 From: Erdenezul Batmunkh Date: Mon, 31 Jul 2017 18:32:34 +0800 Subject: [PATCH 11/12] Add clear comment and tests for positional push #1565 --- mongoengine/queryset/transform.py | 5 +++-- tests/queryset/modify.py | 33 +++++++++++++++++++------------ tests/queryset/queryset.py | 3 +-- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/mongoengine/queryset/transform.py b/mongoengine/queryset/transform.py index b01d3d41..a9907ada 100644 --- a/mongoengine/queryset/transform.py +++ b/mongoengine/queryset/transform.py @@ -336,10 +336,11 @@ def update(_doc_cls=None, **update): elif op == 'addToSet' and isinstance(value, list): value = {key: {'$each': value}} elif op == 'push': - if parts[-1].isdigit() and op == 'push': + if parts[-1].isdigit(): key = parts[0] position = int(parts[-1]) - # position modifier must appear with each. + # $position expects an iterable. If pushing a single value, + # wrap it in a list. if not isinstance(value, (set, tuple, list)): value = [value] value = {key: {'$each': value, '$position': position}} diff --git a/tests/queryset/modify.py b/tests/queryset/modify.py index 44bdc3ff..fe410d19 100644 --- a/tests/queryset/modify.py +++ b/tests/queryset/modify.py @@ -99,26 +99,33 @@ class FindAndModifyTest(unittest.TestCase): @needs_mongodb_v26 def test_modify_with_push(self): class BlogPost(Document): - id = StringField(primary_key=True) tags = ListField(StringField()) BlogPost.drop_collection() BlogPost(id="ABC").save() - BlogPost(id="BCD").save() - blog = BlogPost.objects(id="ABC").modify(push__tags="code") - self.assertEqual(blog.to_mongo(), {"_id": "ABC", "tags": []}) - docs = [{"_id": "ABC", "tags":["code"]}, {"_id": "BCD", "tags":[]}] - self.assertEqual(list(BlogPost._collection.find().sort("id")), docs) + # Push a new tag via modify with new=False (default). + blog = BlogPost(pk='ABC').modify(push__tags='code') + self.assertEqual(blog.tags, []) + blog.reload() + self.assertEqual(blog.tags, ['code']) - another_blog = BlogPost.objects(id="BCD").modify(push__tags="java") - self.assertEqual(another_blog.to_mongo(), {"_id": "BCD", "tags": []}) - another_blog = BlogPost.objects(id="BCD").modify(push__tags__0=["python"]) - self.assertEqual(another_blog.to_mongo(), {"_id": "BCD", "tags": ["java"]}) - docs = [{"_id": "ABC", "tags":["code"]}, - {"_id": "BCD", "tags":["python", "java"]}] - self.assertEqual(list(BlogPost._collection.find().sort("id")), docs) + # Push a new tag via modify with new=True. + blog = BlogPost.objects(pk='ABC').modify(push__tags='java', new=True) + self.assertEqual(blog.tags, ['code', 'java']) + + # Push a new tag with a positional argument. + blog = BlogPost.objects(pk='ABC').modify( + push__tags__0='python', + new=True) + self.assertEqual(blog.tags, ['python', 'code', 'java']) + + # Push multiple new tags with a positional argument. + blog = BlogPost.objects(pk='ABC').modify( + push__tags__1=['go', 'rust'], + new=True) + self.assertEqual(blog.tags, ['python', 'go', 'rust', 'code', 'java']) if __name__ == '__main__': diff --git a/tests/queryset/queryset.py b/tests/queryset/queryset.py index fe97b765..c78ed985 100644 --- a/tests/queryset/queryset.py +++ b/tests/queryset/queryset.py @@ -1913,8 +1913,7 @@ class QuerySetTest(unittest.TestCase): BlogPost.drop_collection() - post = BlogPost(slug="test") - post.save() + post = BlogPost.objects.create(slug="test") BlogPost.objects.filter(id=post.id).update(push__tags="code") BlogPost.objects.filter(id=post.id).update(push__tags__0=["mongodb", "python"]) From f09256a24e83623b194792234f6a9e4c6944e2a6 Mon Sep 17 00:00:00 2001 From: Erdenezul Batmunkh Date: Mon, 31 Jul 2017 18:49:52 +0800 Subject: [PATCH 12/12] Fix modify tests #1565 --- tests/queryset/modify.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/queryset/modify.py b/tests/queryset/modify.py index fe410d19..b37f9b73 100644 --- a/tests/queryset/modify.py +++ b/tests/queryset/modify.py @@ -103,26 +103,26 @@ class FindAndModifyTest(unittest.TestCase): BlogPost.drop_collection() - BlogPost(id="ABC").save() + blog = BlogPost.objects.create() # Push a new tag via modify with new=False (default). - blog = BlogPost(pk='ABC').modify(push__tags='code') + BlogPost(id=blog.id).modify(push__tags='code') self.assertEqual(blog.tags, []) blog.reload() self.assertEqual(blog.tags, ['code']) # Push a new tag via modify with new=True. - blog = BlogPost.objects(pk='ABC').modify(push__tags='java', new=True) + blog = BlogPost.objects(id=blog.id).modify(push__tags='java', new=True) self.assertEqual(blog.tags, ['code', 'java']) # Push a new tag with a positional argument. - blog = BlogPost.objects(pk='ABC').modify( + blog = BlogPost.objects(id=blog.id).modify( push__tags__0='python', new=True) self.assertEqual(blog.tags, ['python', 'code', 'java']) # Push multiple new tags with a positional argument. - blog = BlogPost.objects(pk='ABC').modify( + blog = BlogPost.objects(id=blog.id).modify( push__tags__1=['go', 'rust'], new=True) self.assertEqual(blog.tags, ['python', 'go', 'rust', 'code', 'java'])