Implementing Write Concern
Added write_options dict to save, update, update_one and get_or_create. Thanks to justquick for the initial ticket and code. Refs #132
This commit is contained in:
parent
b3251818cc
commit
40b69baa29
@ -40,44 +40,54 @@ class Document(BaseDocument):
|
|||||||
presence of `_cls` and `_types`, set :attr:`allow_inheritance` to
|
presence of `_cls` and `_types`, set :attr:`allow_inheritance` to
|
||||||
``False`` in the :attr:`meta` dictionary.
|
``False`` in the :attr:`meta` dictionary.
|
||||||
|
|
||||||
A :class:`~mongoengine.Document` may use a **Capped Collection** by
|
A :class:`~mongoengine.Document` may use a **Capped Collection** by
|
||||||
specifying :attr:`max_documents` and :attr:`max_size` in the :attr:`meta`
|
specifying :attr:`max_documents` and :attr:`max_size` in the :attr:`meta`
|
||||||
dictionary. :attr:`max_documents` is the maximum number of documents that
|
dictionary. :attr:`max_documents` is the maximum number of documents that
|
||||||
is allowed to be stored in the collection, and :attr:`max_size` is the
|
is allowed to be stored in the collection, and :attr:`max_size` is the
|
||||||
maximum size of the collection in bytes. If :attr:`max_size` is not
|
maximum size of the collection in bytes. If :attr:`max_size` is not
|
||||||
specified and :attr:`max_documents` is, :attr:`max_size` defaults to
|
specified and :attr:`max_documents` is, :attr:`max_size` defaults to
|
||||||
10000000 bytes (10MB).
|
10000000 bytes (10MB).
|
||||||
|
|
||||||
Indexes may be created by specifying :attr:`indexes` in the :attr:`meta`
|
Indexes may be created by specifying :attr:`indexes` in the :attr:`meta`
|
||||||
dictionary. The value should be a list of field names or tuples of field
|
dictionary. The value should be a list of field names or tuples of field
|
||||||
names. Index direction may be specified by prefixing the field names with
|
names. Index direction may be specified by prefixing the field names with
|
||||||
a **+** or **-** sign.
|
a **+** or **-** sign.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__metaclass__ = TopLevelDocumentMetaclass
|
__metaclass__ = TopLevelDocumentMetaclass
|
||||||
|
|
||||||
def save(self, safe=True, force_insert=False, validate=True):
|
def save(self, safe=True, force_insert=False, validate=True, write_options=None):
|
||||||
"""Save the :class:`~mongoengine.Document` to the database. If the
|
"""Save the :class:`~mongoengine.Document` to the database. If the
|
||||||
document already exists, it will be updated, otherwise it will be
|
document already exists, it will be updated, otherwise it will be
|
||||||
created.
|
created.
|
||||||
|
|
||||||
If ``safe=True`` and the operation is unsuccessful, an
|
If ``safe=True`` and the operation is unsuccessful, an
|
||||||
:class:`~mongoengine.OperationError` will be raised.
|
:class:`~mongoengine.OperationError` will be raised.
|
||||||
|
|
||||||
:param safe: check if the operation succeeded before returning
|
:param safe: check if the operation succeeded before returning
|
||||||
:param force_insert: only try to create a new document, don't allow
|
:param force_insert: only try to create a new document, don't allow
|
||||||
updates of existing documents
|
updates of existing documents
|
||||||
:param validate: validates the document; set to ``False`` to skip.
|
:param validate: validates the document; set to ``False`` to skip.
|
||||||
|
:param write_options: Extra keyword arguments are passed down to
|
||||||
|
:meth:`~pymongo.collection.Collection.save` OR
|
||||||
|
:meth:`~pymongo.collection.Collection.insert`
|
||||||
|
which will be used as options for the resultant ``getLastError`` command.
|
||||||
|
For example, ``save(..., w=2, fsync=True)`` will wait until at least two servers
|
||||||
|
have recorded the write and will force an fsync on each server being written to.
|
||||||
"""
|
"""
|
||||||
if validate:
|
if validate:
|
||||||
self.validate()
|
self.validate()
|
||||||
|
|
||||||
|
if not write_options:
|
||||||
|
write_options = {}
|
||||||
|
|
||||||
doc = self.to_mongo()
|
doc = self.to_mongo()
|
||||||
try:
|
try:
|
||||||
collection = self.__class__.objects._collection
|
collection = self.__class__.objects._collection
|
||||||
if force_insert:
|
if force_insert:
|
||||||
object_id = collection.insert(doc, safe=safe)
|
object_id = collection.insert(doc, safe=safe, **write_options)
|
||||||
else:
|
else:
|
||||||
object_id = collection.save(doc, safe=safe)
|
object_id = collection.save(doc, safe=safe, **write_options)
|
||||||
except pymongo.errors.OperationFailure, err:
|
except pymongo.errors.OperationFailure, err:
|
||||||
message = 'Could not save document (%s)'
|
message = 'Could not save document (%s)'
|
||||||
if u'duplicate key' in unicode(err):
|
if u'duplicate key' in unicode(err):
|
||||||
@ -131,9 +141,9 @@ class MapReduceDocument(object):
|
|||||||
"""A document returned from a map/reduce query.
|
"""A document returned from a map/reduce query.
|
||||||
|
|
||||||
:param collection: An instance of :class:`~pymongo.Collection`
|
:param collection: An instance of :class:`~pymongo.Collection`
|
||||||
:param key: Document/result key, often an instance of
|
:param key: Document/result key, often an instance of
|
||||||
:class:`~pymongo.objectid.ObjectId`. If supplied as
|
:class:`~pymongo.objectid.ObjectId`. If supplied as
|
||||||
an ``ObjectId`` found in the given ``collection``,
|
an ``ObjectId`` found in the given ``collection``,
|
||||||
the object can be accessed via the ``object`` property.
|
the object can be accessed via the ``object`` property.
|
||||||
:param value: The result(s) for this key.
|
:param value: The result(s) for this key.
|
||||||
|
|
||||||
@ -148,7 +158,7 @@ class MapReduceDocument(object):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def object(self):
|
def object(self):
|
||||||
"""Lazy-load the object referenced by ``self.key``. ``self.key``
|
"""Lazy-load the object referenced by ``self.key``. ``self.key``
|
||||||
should be the ``primary_key``.
|
should be the ``primary_key``.
|
||||||
"""
|
"""
|
||||||
id_field = self._document()._meta['id_field']
|
id_field = self._document()._meta['id_field']
|
||||||
|
@ -643,7 +643,7 @@ class QuerySet(object):
|
|||||||
raise self._document.DoesNotExist("%s matching query does not exist."
|
raise self._document.DoesNotExist("%s matching query does not exist."
|
||||||
% self._document._class_name)
|
% self._document._class_name)
|
||||||
|
|
||||||
def get_or_create(self, *q_objs, **query):
|
def get_or_create(self, write_options=None, *q_objs, **query):
|
||||||
"""Retrieve unique object or create, if it doesn't exist. Returns a tuple of
|
"""Retrieve unique object or create, if it doesn't exist. Returns a tuple of
|
||||||
``(object, created)``, where ``object`` is the retrieved or created object
|
``(object, created)``, where ``object`` is the retrieved or created object
|
||||||
and ``created`` is a boolean specifying whether a new object was created. Raises
|
and ``created`` is a boolean specifying whether a new object was created. Raises
|
||||||
@ -653,6 +653,10 @@ class QuerySet(object):
|
|||||||
dictionary of default values for the new document may be provided as a
|
dictionary of default values for the new document may be provided as a
|
||||||
keyword argument called :attr:`defaults`.
|
keyword argument called :attr:`defaults`.
|
||||||
|
|
||||||
|
:param write_options: optional extra keyword arguments used if we
|
||||||
|
have to create a new document.
|
||||||
|
Passes any write_options onto :meth:`~mongoengine.document.Document.save`
|
||||||
|
|
||||||
.. versionadded:: 0.3
|
.. versionadded:: 0.3
|
||||||
"""
|
"""
|
||||||
defaults = query.get('defaults', {})
|
defaults = query.get('defaults', {})
|
||||||
@ -664,7 +668,7 @@ class QuerySet(object):
|
|||||||
if count == 0:
|
if count == 0:
|
||||||
query.update(defaults)
|
query.update(defaults)
|
||||||
doc = self._document(**query)
|
doc = self._document(**query)
|
||||||
doc.save()
|
doc.save(write_options=write_options)
|
||||||
return doc, True
|
return doc, True
|
||||||
elif count == 1:
|
elif count == 1:
|
||||||
return self.first(), False
|
return self.first(), False
|
||||||
@ -1055,22 +1059,27 @@ class QuerySet(object):
|
|||||||
|
|
||||||
return mongo_update
|
return mongo_update
|
||||||
|
|
||||||
def update(self, safe_update=True, upsert=False, **update):
|
def update(self, safe_update=True, upsert=False, 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.
|
||||||
|
|
||||||
:param safe: check if the operation succeeded before returning
|
:param safe_update: check if the operation succeeded before returning
|
||||||
:param update: Django-style update keyword arguments
|
:param upsert: Any existing document with that "_id" is overwritten.
|
||||||
|
:param write_options: extra keyword arguments for :meth:`~pymongo.collection.Collection.update`
|
||||||
|
|
||||||
.. versionadded:: 0.2
|
.. versionadded:: 0.2
|
||||||
"""
|
"""
|
||||||
if pymongo.version < '1.1.1':
|
if pymongo.version < '1.1.1':
|
||||||
raise OperationError('update() method requires PyMongo 1.1.1+')
|
raise OperationError('update() method requires PyMongo 1.1.1+')
|
||||||
|
|
||||||
|
if not write_options:
|
||||||
|
write_options = {}
|
||||||
|
|
||||||
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=True,
|
||||||
upsert=upsert, safe=safe_update)
|
upsert=upsert, safe=safe_update,
|
||||||
|
**write_options)
|
||||||
if ret is not None and 'n' in ret:
|
if ret is not None and 'n' in ret:
|
||||||
return ret['n']
|
return ret['n']
|
||||||
except pymongo.errors.OperationFailure, err:
|
except pymongo.errors.OperationFailure, err:
|
||||||
@ -1079,22 +1088,27 @@ class QuerySet(object):
|
|||||||
raise OperationError(message)
|
raise OperationError(message)
|
||||||
raise OperationError(u'Update failed (%s)' % unicode(err))
|
raise OperationError(u'Update failed (%s)' % unicode(err))
|
||||||
|
|
||||||
def update_one(self, safe_update=True, upsert=False, **update):
|
def update_one(self, safe_update=True, upsert=False, write_options=None, **update):
|
||||||
"""Perform an atomic update on first field matched by the query. When
|
"""Perform an atomic update on first field 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.
|
||||||
|
|
||||||
:param safe: check if the operation succeeded before returning
|
:param safe_update: check if the operation succeeded before returning
|
||||||
|
:param upsert: Any existing document with that "_id" is overwritten.
|
||||||
|
:param write_options: extra keyword arguments for :meth:`~pymongo.collection.Collection.update`
|
||||||
:param update: Django-style update keyword arguments
|
:param update: Django-style update keyword arguments
|
||||||
|
|
||||||
.. versionadded:: 0.2
|
.. versionadded:: 0.2
|
||||||
"""
|
"""
|
||||||
|
if not write_options:
|
||||||
|
write_options = {}
|
||||||
update = QuerySet._transform_update(self._document, **update)
|
update = QuerySet._transform_update(self._document, **update)
|
||||||
try:
|
try:
|
||||||
# Explicitly provide 'multi=False' to newer versions of PyMongo
|
# Explicitly provide 'multi=False' to newer versions of PyMongo
|
||||||
# as the default may change to 'True'
|
# as the default may change to 'True'
|
||||||
if pymongo.version >= '1.1.1':
|
if pymongo.version >= '1.1.1':
|
||||||
ret = self._collection.update(self._query, update, multi=False,
|
ret = self._collection.update(self._query, update, multi=False,
|
||||||
upsert=upsert, safe=safe_update)
|
upsert=upsert, safe=safe_update,
|
||||||
|
**write_options)
|
||||||
else:
|
else:
|
||||||
# Older versions of PyMongo don't support 'multi'
|
# Older versions of PyMongo don't support 'multi'
|
||||||
ret = self._collection.update(self._query, update,
|
ret = self._collection.update(self._query, update,
|
||||||
|
@ -898,6 +898,26 @@ class DocumentTest(unittest.TestCase):
|
|||||||
pickle_doc.reload()
|
pickle_doc.reload()
|
||||||
self.assertEquals(resurrected, pickle_doc)
|
self.assertEquals(resurrected, pickle_doc)
|
||||||
|
|
||||||
|
def test_write_options(self):
|
||||||
|
"""Test that passing write_options works"""
|
||||||
|
|
||||||
|
self.Person.drop_collection()
|
||||||
|
|
||||||
|
write_options = {"fsync": True}
|
||||||
|
|
||||||
|
author, created = self.Person.objects.get_or_create(
|
||||||
|
name='Test User', write_options=write_options)
|
||||||
|
author.save(write_options=write_options)
|
||||||
|
|
||||||
|
self.Person.objects.update(set__name='Ross', write_options=write_options)
|
||||||
|
|
||||||
|
author = self.Person.objects.first()
|
||||||
|
self.assertEquals(author.name, 'Ross')
|
||||||
|
|
||||||
|
self.Person.objects.update_one(set__name='Test User', write_options=write_options)
|
||||||
|
author = self.Person.objects.first()
|
||||||
|
self.assertEquals(author.name, 'Test User')
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user