parent
09c32a63ce
commit
14be7ba2e2
@ -5,6 +5,7 @@ Changelog
|
|||||||
Changes in dev
|
Changes in dev
|
||||||
==============
|
==============
|
||||||
|
|
||||||
|
- Added support for the positional operator
|
||||||
- Updated geo index checking to be recursive and check in embedded documents
|
- Updated geo index checking to be recursive and check in embedded documents
|
||||||
- Updated default collection naming convention
|
- Updated default collection naming convention
|
||||||
- Added Document Mixin support
|
- Added Document Mixin support
|
||||||
|
@ -467,3 +467,19 @@ modifier comes before the field, not after it::
|
|||||||
>>> post.reload()
|
>>> post.reload()
|
||||||
>>> post.tags
|
>>> post.tags
|
||||||
['database', 'nosql']
|
['database', 'nosql']
|
||||||
|
|
||||||
|
The positional operator allows you to update list items without knowing the
|
||||||
|
index position, therefore making the update a single atomic operation. As we
|
||||||
|
cannot use the `$` syntax in keyword arguments it has been mapped to `S`::
|
||||||
|
|
||||||
|
>>> post = BlogPost(title='Test', page_views=0, tags=['database', 'mongo'])
|
||||||
|
>>> post.save()
|
||||||
|
>>> BlogPost.objects(id=post.id, tags='mongo').update(set__tags__S='mongodb')
|
||||||
|
>>> post.reload()
|
||||||
|
>>> post.tags
|
||||||
|
['database', 'mongodb']
|
||||||
|
|
||||||
|
.. note ::
|
||||||
|
Currently only top level lists are handled, future versions of mongodb /
|
||||||
|
pymongo plan to support nested positional operators. See `The $ positional
|
||||||
|
operator <http://www.mongodb.org/display/DOCS/Updating#Updating-The%24positionaloperator>`_.
|
@ -1215,6 +1215,9 @@ class QuerySet(object):
|
|||||||
append_field = True
|
append_field = True
|
||||||
for field in fields:
|
for field in fields:
|
||||||
if isinstance(field, str):
|
if isinstance(field, str):
|
||||||
|
# Convert the S operator to $
|
||||||
|
if field == 'S':
|
||||||
|
field = '$'
|
||||||
parts.append(field)
|
parts.append(field)
|
||||||
append_field = False
|
append_field = False
|
||||||
else:
|
else:
|
||||||
@ -1243,7 +1246,7 @@ class QuerySet(object):
|
|||||||
|
|
||||||
return mongo_update
|
return mongo_update
|
||||||
|
|
||||||
def update(self, safe_update=True, upsert=False, write_options=None, **update):
|
def update(self, safe_update=True, upsert=False, multi=True, write_options=None, **update):
|
||||||
"""Perform an atomic update on the fields matched by the query. When
|
"""Perform an atomic update on the fields matched by the query. When
|
||||||
``safe_update`` is used, the number of affected documents is returned.
|
``safe_update`` is used, the number of affected documents is returned.
|
||||||
|
|
||||||
@ -1261,7 +1264,7 @@ class QuerySet(object):
|
|||||||
|
|
||||||
update = QuerySet._transform_update(self._document, **update)
|
update = QuerySet._transform_update(self._document, **update)
|
||||||
try:
|
try:
|
||||||
ret = self._collection.update(self._query, update, multi=True,
|
ret = self._collection.update(self._query, update, multi=multi,
|
||||||
upsert=upsert, safe=safe_update,
|
upsert=upsert, safe=safe_update,
|
||||||
**write_options)
|
**write_options)
|
||||||
if ret is not None and 'n' in ret:
|
if ret is not None and 'n' in ret:
|
||||||
|
@ -260,6 +260,77 @@ class QuerySetTest(unittest.TestCase):
|
|||||||
|
|
||||||
Blog.drop_collection()
|
Blog.drop_collection()
|
||||||
|
|
||||||
|
def test_update_using_positional_operator(self):
|
||||||
|
"""Ensure that the list fields can be updated using the positional
|
||||||
|
operator."""
|
||||||
|
|
||||||
|
class Comment(EmbeddedDocument):
|
||||||
|
by = StringField()
|
||||||
|
votes = IntField()
|
||||||
|
|
||||||
|
class BlogPost(Document):
|
||||||
|
title = StringField()
|
||||||
|
comments = ListField(EmbeddedDocumentField(Comment))
|
||||||
|
|
||||||
|
BlogPost.drop_collection()
|
||||||
|
|
||||||
|
c1 = Comment(by="joe", votes=3)
|
||||||
|
c2 = Comment(by="jane", votes=7)
|
||||||
|
|
||||||
|
BlogPost(title="ABC", comments=[c1, c2]).save()
|
||||||
|
|
||||||
|
BlogPost.objects(comments__by="joe").update(inc__comments__S__votes=1)
|
||||||
|
|
||||||
|
post = BlogPost.objects.first()
|
||||||
|
self.assertEquals(post.comments[0].by, 'joe')
|
||||||
|
self.assertEquals(post.comments[0].votes, 4)
|
||||||
|
|
||||||
|
# Currently the $ operator only applies to the first matched item in
|
||||||
|
# the query
|
||||||
|
|
||||||
|
class Simple(Document):
|
||||||
|
x = ListField()
|
||||||
|
|
||||||
|
Simple.drop_collection()
|
||||||
|
Simple(x=[1, 2, 3, 2]).save()
|
||||||
|
Simple.objects(x=2).update(inc__x__S=1)
|
||||||
|
|
||||||
|
simple = Simple.objects.first()
|
||||||
|
self.assertEquals(simple.x, [1, 3, 3, 2])
|
||||||
|
Simple.drop_collection()
|
||||||
|
|
||||||
|
# You can set multiples
|
||||||
|
Simple.drop_collection()
|
||||||
|
Simple(x=[1, 2, 3, 4]).save()
|
||||||
|
Simple(x=[2, 3, 4, 5]).save()
|
||||||
|
Simple(x=[3, 4, 5, 6]).save()
|
||||||
|
Simple(x=[4, 5, 6, 7]).save()
|
||||||
|
Simple.objects(x=3).update(set__x__S=0)
|
||||||
|
|
||||||
|
s = Simple.objects()
|
||||||
|
self.assertEquals(s[0].x, [1, 2, 0, 4])
|
||||||
|
self.assertEquals(s[1].x, [2, 0, 4, 5])
|
||||||
|
self.assertEquals(s[2].x, [0, 4, 5, 6])
|
||||||
|
self.assertEquals(s[3].x, [4, 5, 6, 7])
|
||||||
|
|
||||||
|
# Using "$unset" with an expression like this "array.$" will result in
|
||||||
|
# the array item becoming None, not being removed.
|
||||||
|
Simple.drop_collection()
|
||||||
|
Simple(x=[1, 2, 3, 4, 3, 2, 3, 4]).save()
|
||||||
|
Simple.objects(x=3).update(unset__x__S=1)
|
||||||
|
simple = Simple.objects.first()
|
||||||
|
self.assertEquals(simple.x, [1, 2, None, 4, 3, 2, 3, 4])
|
||||||
|
|
||||||
|
# Nested updates arent supported yet..
|
||||||
|
def update_nested():
|
||||||
|
Simple.drop_collection()
|
||||||
|
Simple(x=[{'test': [1, 2, 3, 4]}]).save()
|
||||||
|
Simple.objects(x__test=2).update(set__x__S__test__S=3)
|
||||||
|
self.assertEquals(simple.x, [1, 2, 3, 4])
|
||||||
|
|
||||||
|
self.assertRaises(OperationError, update_nested)
|
||||||
|
Simple.drop_collection()
|
||||||
|
|
||||||
def test_mapfield_update(self):
|
def test_mapfield_update(self):
|
||||||
"""Ensure that the MapField can be updated."""
|
"""Ensure that the MapField can be updated."""
|
||||||
class Member(EmbeddedDocument):
|
class Member(EmbeddedDocument):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user