Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af292b0ec2 | ||
|
|
1ead7f9b2b | ||
|
|
5c91877b69 | ||
|
|
b1002dd4f9 | ||
|
|
a4fe091a51 | ||
|
|
216217e2c6 | ||
|
|
799775b3a7 | ||
|
|
ae0384df29 | ||
|
|
8f57279dc7 |
@@ -102,5 +102,5 @@ deploy:
|
|||||||
on:
|
on:
|
||||||
tags: true
|
tags: true
|
||||||
repo: MongoEngine/mongoengine
|
repo: MongoEngine/mongoengine
|
||||||
condition: ($PYMONGO = 3.x) && ($MONGODB = 3.4)
|
condition: ($PYMONGO = 3.x) && ($MONGODB = 3.4.17)
|
||||||
python: 2.7
|
python: 2.7
|
||||||
|
|||||||
@@ -7,6 +7,11 @@ Development
|
|||||||
===========
|
===========
|
||||||
- (Fill this out as you fix issues and develop your features).
|
- (Fill this out as you fix issues and develop your features).
|
||||||
|
|
||||||
|
Changes in 0.18.2
|
||||||
|
=================
|
||||||
|
- Replace some of the deprecated PyMongo v2.x methods with their v3.x equivalents #2097
|
||||||
|
- Various code clarity and documentation improvements
|
||||||
|
|
||||||
Changes in 0.18.1
|
Changes in 0.18.1
|
||||||
=================
|
=================
|
||||||
- Fix a bug introduced in 0.18.0 which was causing `.save()` to update all the fields
|
- Fix a bug introduced in 0.18.0 which was causing `.save()` to update all the fields
|
||||||
|
|||||||
@@ -714,11 +714,16 @@ subsequent calls to :meth:`~mongoengine.queryset.QuerySet.order_by`. ::
|
|||||||
Shard keys
|
Shard keys
|
||||||
==========
|
==========
|
||||||
|
|
||||||
If your collection is sharded, then you need to specify the shard key as a tuple,
|
If your collection is sharded by multiple keys, then you can improve shard
|
||||||
using the :attr:`shard_key` attribute of :attr:`~mongoengine.Document.meta`.
|
routing (and thus the performance of your application) by specifying the shard
|
||||||
This ensures that the shard key is sent with the query when calling the
|
key, using the :attr:`shard_key` attribute of
|
||||||
:meth:`~mongoengine.document.Document.save` or
|
:attr:`~mongoengine.Document.meta`. The shard key should be defined as a tuple.
|
||||||
:meth:`~mongoengine.document.Document.update` method on an existing
|
|
||||||
|
This ensures that the full shard key is sent with the query when calling
|
||||||
|
methods such as :meth:`~mongoengine.document.Document.save`,
|
||||||
|
:meth:`~mongoengine.document.Document.update`,
|
||||||
|
:meth:`~mongoengine.document.Document.modify`, or
|
||||||
|
:meth:`~mongoengine.document.Document.delete` on an existing
|
||||||
:class:`~mongoengine.Document` instance::
|
:class:`~mongoengine.Document` instance::
|
||||||
|
|
||||||
class LogEntry(Document):
|
class LogEntry(Document):
|
||||||
@@ -728,7 +733,8 @@ This ensures that the shard key is sent with the query when calling the
|
|||||||
data = StringField()
|
data = StringField()
|
||||||
|
|
||||||
meta = {
|
meta = {
|
||||||
'shard_key': ('machine', 'timestamp',)
|
'shard_key': ('machine', 'timestamp'),
|
||||||
|
'indexes': ('machine', 'timestamp'),
|
||||||
}
|
}
|
||||||
|
|
||||||
.. _document-inheritance:
|
.. _document-inheritance:
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ __all__ = (list(document.__all__) + list(fields.__all__) +
|
|||||||
list(signals.__all__) + list(errors.__all__))
|
list(signals.__all__) + list(errors.__all__))
|
||||||
|
|
||||||
|
|
||||||
VERSION = (0, 18, 1)
|
VERSION = (0, 18, 2)
|
||||||
|
|
||||||
|
|
||||||
def get_version():
|
def get_version():
|
||||||
|
|||||||
@@ -11,18 +11,20 @@ __all__ = ('BaseDict', 'StrictDict', 'BaseList', 'EmbeddedDocumentList', 'LazyRe
|
|||||||
|
|
||||||
|
|
||||||
def mark_as_changed_wrapper(parent_method):
|
def mark_as_changed_wrapper(parent_method):
|
||||||
"""Decorators that ensures _mark_as_changed method gets called"""
|
"""Decorator that ensures _mark_as_changed method gets called."""
|
||||||
def wrapper(self, *args, **kwargs):
|
def wrapper(self, *args, **kwargs):
|
||||||
result = parent_method(self, *args, **kwargs) # Can't use super() in the decorator
|
# Can't use super() in the decorator.
|
||||||
|
result = parent_method(self, *args, **kwargs)
|
||||||
self._mark_as_changed()
|
self._mark_as_changed()
|
||||||
return result
|
return result
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
def mark_key_as_changed_wrapper(parent_method):
|
def mark_key_as_changed_wrapper(parent_method):
|
||||||
"""Decorators that ensures _mark_as_changed method gets called with the key argument"""
|
"""Decorator that ensures _mark_as_changed method gets called with the key argument"""
|
||||||
def wrapper(self, key, *args, **kwargs):
|
def wrapper(self, key, *args, **kwargs):
|
||||||
result = parent_method(self, key, *args, **kwargs) # Can't use super() in the decorator
|
# Can't use super() in the decorator.
|
||||||
|
result = parent_method(self, key, *args, **kwargs)
|
||||||
self._mark_as_changed(key)
|
self._mark_as_changed(key)
|
||||||
return result
|
return result
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|||||||
@@ -128,10 +128,9 @@ class BaseField(object):
|
|||||||
return instance._data.get(self.name)
|
return instance._data.get(self.name)
|
||||||
|
|
||||||
def __set__(self, instance, value):
|
def __set__(self, instance, value):
|
||||||
"""Descriptor for assigning a value to a field in a document.
|
"""Descriptor for assigning a value to a field in a document."""
|
||||||
"""
|
# If setting to None and there is a default value provided for this
|
||||||
# If setting to None and there is a default
|
# field, then set the value to the default value.
|
||||||
# Then set the value to the default value
|
|
||||||
if value is None:
|
if value is None:
|
||||||
if self.null:
|
if self.null:
|
||||||
value = None
|
value = None
|
||||||
@@ -142,12 +141,16 @@ class BaseField(object):
|
|||||||
|
|
||||||
if instance._initialised:
|
if instance._initialised:
|
||||||
try:
|
try:
|
||||||
if (self.name not in instance._data or
|
value_has_changed = (
|
||||||
instance._data[self.name] != value):
|
self.name not in instance._data or
|
||||||
|
instance._data[self.name] != value
|
||||||
|
)
|
||||||
|
if value_has_changed:
|
||||||
instance._mark_as_changed(self.name)
|
instance._mark_as_changed(self.name)
|
||||||
except Exception:
|
except Exception:
|
||||||
# Values cant be compared eg: naive and tz datetimes
|
# Some values can't be compared and throw an error when we
|
||||||
# So mark it as changed
|
# attempt to do so (e.g. tz-naive and tz-aware datetimes).
|
||||||
|
# Mark the field as changed in such cases.
|
||||||
instance._mark_as_changed(self.name)
|
instance._mark_as_changed(self.name)
|
||||||
|
|
||||||
EmbeddedDocument = _import_class('EmbeddedDocument')
|
EmbeddedDocument = _import_class('EmbeddedDocument')
|
||||||
@@ -157,6 +160,7 @@ class BaseField(object):
|
|||||||
for v in value:
|
for v in value:
|
||||||
if isinstance(v, EmbeddedDocument):
|
if isinstance(v, EmbeddedDocument):
|
||||||
v._instance = weakref.proxy(instance)
|
v._instance = weakref.proxy(instance)
|
||||||
|
|
||||||
instance._data[self.name] = value
|
instance._data[self.name] = value
|
||||||
|
|
||||||
def error(self, message='', errors=None, field_name=None):
|
def error(self, message='', errors=None, field_name=None):
|
||||||
|
|||||||
@@ -544,7 +544,7 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def _qs(self):
|
def _qs(self):
|
||||||
"""Return the queryset to use for updating / reloading / deletions."""
|
"""Return the default queryset corresponding to this document."""
|
||||||
if not hasattr(self, '__objects'):
|
if not hasattr(self, '__objects'):
|
||||||
self.__objects = QuerySet(self, self._get_collection())
|
self.__objects = QuerySet(self, self._get_collection())
|
||||||
return self.__objects
|
return self.__objects
|
||||||
@@ -552,9 +552,11 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
|
|||||||
@property
|
@property
|
||||||
def _object_key(self):
|
def _object_key(self):
|
||||||
"""Get the query dict that can be used to fetch this object from
|
"""Get the query dict that can be used to fetch this object from
|
||||||
the database. Most of the time it's a simple PK lookup, but in
|
the database.
|
||||||
case of a sharded collection with a compound shard key, it can
|
|
||||||
contain a more complex query.
|
Most of the time the dict is a simple PK lookup, but in case of
|
||||||
|
a sharded collection with a compound shard key, it can contain a more
|
||||||
|
complex query.
|
||||||
"""
|
"""
|
||||||
select_dict = {'pk': self.pk}
|
select_dict = {'pk': self.pk}
|
||||||
shard_key = self.__class__._meta.get('shard_key', tuple())
|
shard_key = self.__class__._meta.get('shard_key', tuple())
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from operator import itemgetter
|
|||||||
from bson import Binary, DBRef, ObjectId, SON
|
from bson import Binary, DBRef, ObjectId, SON
|
||||||
import gridfs
|
import gridfs
|
||||||
import pymongo
|
import pymongo
|
||||||
|
from pymongo import ReturnDocument
|
||||||
import six
|
import six
|
||||||
from six import iteritems
|
from six import iteritems
|
||||||
|
|
||||||
@@ -1964,9 +1965,11 @@ class SequenceField(BaseField):
|
|||||||
sequence_name = self.get_sequence_name()
|
sequence_name = self.get_sequence_name()
|
||||||
sequence_id = '%s.%s' % (sequence_name, self.name)
|
sequence_id = '%s.%s' % (sequence_name, self.name)
|
||||||
collection = get_db(alias=self.db_alias)[self.collection_name]
|
collection = get_db(alias=self.db_alias)[self.collection_name]
|
||||||
counter = collection.find_and_modify(query={'_id': sequence_id},
|
|
||||||
|
counter = collection.find_one_and_update(
|
||||||
|
filter={'_id': sequence_id},
|
||||||
update={'$inc': {'next': 1}},
|
update={'$inc': {'next': 1}},
|
||||||
new=True,
|
return_document=ReturnDocument.AFTER,
|
||||||
upsert=True)
|
upsert=True)
|
||||||
return self.value_decorator(counter['next'])
|
return self.value_decorator(counter['next'])
|
||||||
|
|
||||||
@@ -1975,9 +1978,10 @@ class SequenceField(BaseField):
|
|||||||
sequence_name = self.get_sequence_name()
|
sequence_name = self.get_sequence_name()
|
||||||
sequence_id = "%s.%s" % (sequence_name, self.name)
|
sequence_id = "%s.%s" % (sequence_name, self.name)
|
||||||
collection = get_db(alias=self.db_alias)[self.collection_name]
|
collection = get_db(alias=self.db_alias)[self.collection_name]
|
||||||
counter = collection.find_and_modify(query={"_id": sequence_id},
|
counter = collection.find_one_and_update(
|
||||||
|
filter={"_id": sequence_id},
|
||||||
update={"$set": {"next": value}},
|
update={"$set": {"next": value}},
|
||||||
new=True,
|
return_document=ReturnDocument.AFTER,
|
||||||
upsert=True)
|
upsert=True)
|
||||||
return self.value_decorator(counter['next'])
|
return self.value_decorator(counter['next'])
|
||||||
|
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ class BaseQuerySet(object):
|
|||||||
self._initial_query = {
|
self._initial_query = {
|
||||||
'_cls': {'$in': self._document._subclasses}}
|
'_cls': {'$in': self._document._subclasses}}
|
||||||
self._loaded_fields = QueryFieldList(always_include=['_cls'])
|
self._loaded_fields = QueryFieldList(always_include=['_cls'])
|
||||||
|
|
||||||
self._cursor_obj = None
|
self._cursor_obj = None
|
||||||
self._limit = None
|
self._limit = None
|
||||||
self._skip = None
|
self._skip = None
|
||||||
@@ -480,9 +481,10 @@ class BaseQuerySet(object):
|
|||||||
write_concern=write_concern,
|
write_concern=write_concern,
|
||||||
**{'pull_all__%s' % field_name: self})
|
**{'pull_all__%s' % field_name: self})
|
||||||
|
|
||||||
result = queryset._collection.remove(queryset._query, **write_concern)
|
with set_write_concern(queryset._collection, write_concern) as collection:
|
||||||
if result:
|
result = collection.delete_many(queryset._query)
|
||||||
return result.get('n')
|
if result.acknowledged:
|
||||||
|
return result.deleted_count
|
||||||
|
|
||||||
def update(self, upsert=False, multi=True, write_concern=None,
|
def update(self, upsert=False, multi=True, write_concern=None,
|
||||||
full_result=False, **update):
|
full_result=False, **update):
|
||||||
@@ -707,8 +709,9 @@ class BaseQuerySet(object):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
def no_sub_classes(self):
|
def no_sub_classes(self):
|
||||||
"""
|
"""Filter for only the instances of this specific document.
|
||||||
Only return instances of this document and not any inherited documents
|
|
||||||
|
Do NOT return any inherited documents.
|
||||||
"""
|
"""
|
||||||
if self._document._meta.get('allow_inheritance') is True:
|
if self._document._meta.get('allow_inheritance') is True:
|
||||||
self._initial_query = {'_cls': self._document._class_name}
|
self._initial_query = {'_cls': self._document._class_name}
|
||||||
|
|||||||
@@ -1857,8 +1857,8 @@ class QuerySetTest(unittest.TestCase):
|
|||||||
self.Person.objects()[:1].delete()
|
self.Person.objects()[:1].delete()
|
||||||
self.assertEqual(1, BlogPost.objects.count())
|
self.assertEqual(1, BlogPost.objects.count())
|
||||||
|
|
||||||
def test_limit_with_write_concern_0(self):
|
def test_delete_edge_case_with_write_concern_0_return_None(self):
|
||||||
|
"""Return None when write is unacknowledged"""
|
||||||
p1 = self.Person(name="User Z", age=20).save()
|
p1 = self.Person(name="User Z", age=20).save()
|
||||||
del_result = p1.delete(w=0)
|
del_result = p1.delete(w=0)
|
||||||
self.assertEqual(None, del_result)
|
self.assertEqual(None, del_result)
|
||||||
|
|||||||
Reference in New Issue
Block a user