Compare commits

...

9 Commits

Author SHA1 Message Date
Stefan Wojcik
af292b0ec2 Bump version to v0.18.2 2019-06-25 16:52:31 +02:00
Stefan Wojcik
1ead7f9b2b Add changelog entries for v0.18.2 2019-06-25 16:51:56 +02:00
Stefan Wojcik
5c91877b69 Fix the Travis deployment condition
See https://github.com/MongoEngine/mongoengine/issues/2104 for details.

For now I'm hardcoding `$MONGODB = 3.4.17` just to get a release out there,
but we should probably use the globals going forward. Will do that in
a follow-up commit once I get the `travis-conditions` gem up and running and
hence can test `.travis.yml` changes without deploying.
2019-06-25 16:48:51 +02:00
Bastien Gérard
b1002dd4f9 Merge pull request #2097 from bagerard/remove_deprecated_pymongo_methods
remove pymongo deprecated methods: find_and_modify & remove
2019-06-24 22:03:58 +02:00
Stefan Wojcik
a4fe091a51 Cleaner code & comments in BaseField.__set__ 2019-06-21 13:51:53 +02:00
Stefan Wojcik
216217e2c6 Datastructures comments: fix typos and tweak formatting [ci skip] 2019-06-21 13:48:24 +02:00
Stefan Wojcik
799775b3a7 Slightly cleaner docstring of BaseQuerySet.no_sub_classes [ci skip] 2019-06-20 12:18:58 +02:00
Stefan Wójcik
ae0384df29 Improve Document.meta.shard_key docs (#2099)
This closes #2096. Previous documentation of the shard_key meta attribute was
missing the crucial point that it really only matters if your collection is
sharded over a compound index.
2019-06-20 11:25:51 +02:00
Bastien Gérard
8f57279dc7 remove pymongo deprecated methods: find_and_modify & remove 2019-06-19 23:04:23 +02:00
10 changed files with 65 additions and 39 deletions

View File

@@ -102,5 +102,5 @@ deploy:
on:
tags: true
repo: MongoEngine/mongoengine
condition: ($PYMONGO = 3.x) && ($MONGODB = 3.4)
condition: ($PYMONGO = 3.x) && ($MONGODB = 3.4.17)
python: 2.7

View File

@@ -7,6 +7,11 @@ Development
===========
- (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
=================
- Fix a bug introduced in 0.18.0 which was causing `.save()` to update all the fields

View File

@@ -714,11 +714,16 @@ subsequent calls to :meth:`~mongoengine.queryset.QuerySet.order_by`. ::
Shard keys
==========
If your collection is sharded, then you need to specify the shard key as a tuple,
using the :attr:`shard_key` attribute of :attr:`~mongoengine.Document.meta`.
This ensures that the shard key is sent with the query when calling the
:meth:`~mongoengine.document.Document.save` or
:meth:`~mongoengine.document.Document.update` method on an existing
If your collection is sharded by multiple keys, then you can improve shard
routing (and thus the performance of your application) by specifying the shard
key, using the :attr:`shard_key` attribute of
:attr:`~mongoengine.Document.meta`. The shard key should be defined as a tuple.
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 LogEntry(Document):
@@ -728,7 +733,8 @@ This ensures that the shard key is sent with the query when calling the
data = StringField()
meta = {
'shard_key': ('machine', 'timestamp',)
'shard_key': ('machine', 'timestamp'),
'indexes': ('machine', 'timestamp'),
}
.. _document-inheritance:

View File

@@ -23,7 +23,7 @@ __all__ = (list(document.__all__) + list(fields.__all__) +
list(signals.__all__) + list(errors.__all__))
VERSION = (0, 18, 1)
VERSION = (0, 18, 2)
def get_version():

View File

@@ -11,18 +11,20 @@ __all__ = ('BaseDict', 'StrictDict', 'BaseList', 'EmbeddedDocumentList', 'LazyRe
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):
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()
return result
return wrapper
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):
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)
return result
return wrapper

View File

@@ -128,10 +128,9 @@ class BaseField(object):
return instance._data.get(self.name)
def __set__(self, instance, value):
"""Descriptor for assigning a value to a field in a document.
"""
# If setting to None and there is a default
# Then set the value to the default value
"""Descriptor for assigning a value to a field in a document."""
# If setting to None and there is a default value provided for this
# field, then set the value to the default value.
if value is None:
if self.null:
value = None
@@ -142,12 +141,16 @@ class BaseField(object):
if instance._initialised:
try:
if (self.name not in instance._data or
instance._data[self.name] != value):
value_has_changed = (
self.name not in instance._data or
instance._data[self.name] != value
)
if value_has_changed:
instance._mark_as_changed(self.name)
except Exception:
# Values cant be compared eg: naive and tz datetimes
# So mark it as changed
# Some values can't be compared and throw an error when we
# 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)
EmbeddedDocument = _import_class('EmbeddedDocument')
@@ -157,6 +160,7 @@ class BaseField(object):
for v in value:
if isinstance(v, EmbeddedDocument):
v._instance = weakref.proxy(instance)
instance._data[self.name] = value
def error(self, message='', errors=None, field_name=None):

View File

@@ -544,7 +544,7 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
@property
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'):
self.__objects = QuerySet(self, self._get_collection())
return self.__objects
@@ -552,9 +552,11 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
@property
def _object_key(self):
"""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
case of a sharded collection with a compound shard key, it can
contain a more complex query.
the database.
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}
shard_key = self.__class__._meta.get('shard_key', tuple())

View File

@@ -10,6 +10,7 @@ from operator import itemgetter
from bson import Binary, DBRef, ObjectId, SON
import gridfs
import pymongo
from pymongo import ReturnDocument
import six
from six import iteritems
@@ -1964,10 +1965,12 @@ class SequenceField(BaseField):
sequence_name = self.get_sequence_name()
sequence_id = '%s.%s' % (sequence_name, self.name)
collection = get_db(alias=self.db_alias)[self.collection_name]
counter = collection.find_and_modify(query={'_id': sequence_id},
update={'$inc': {'next': 1}},
new=True,
upsert=True)
counter = collection.find_one_and_update(
filter={'_id': sequence_id},
update={'$inc': {'next': 1}},
return_document=ReturnDocument.AFTER,
upsert=True)
return self.value_decorator(counter['next'])
def set_next_value(self, value):
@@ -1975,10 +1978,11 @@ class SequenceField(BaseField):
sequence_name = self.get_sequence_name()
sequence_id = "%s.%s" % (sequence_name, self.name)
collection = get_db(alias=self.db_alias)[self.collection_name]
counter = collection.find_and_modify(query={"_id": sequence_id},
update={"$set": {"next": value}},
new=True,
upsert=True)
counter = collection.find_one_and_update(
filter={"_id": sequence_id},
update={"$set": {"next": value}},
return_document=ReturnDocument.AFTER,
upsert=True)
return self.value_decorator(counter['next'])
def get_next_value(self):

View File

@@ -73,6 +73,7 @@ class BaseQuerySet(object):
self._initial_query = {
'_cls': {'$in': self._document._subclasses}}
self._loaded_fields = QueryFieldList(always_include=['_cls'])
self._cursor_obj = None
self._limit = None
self._skip = None
@@ -480,9 +481,10 @@ class BaseQuerySet(object):
write_concern=write_concern,
**{'pull_all__%s' % field_name: self})
result = queryset._collection.remove(queryset._query, **write_concern)
if result:
return result.get('n')
with set_write_concern(queryset._collection, write_concern) as collection:
result = collection.delete_many(queryset._query)
if result.acknowledged:
return result.deleted_count
def update(self, upsert=False, multi=True, write_concern=None,
full_result=False, **update):
@@ -707,8 +709,9 @@ class BaseQuerySet(object):
return queryset
def no_sub_classes(self):
"""
Only return instances of this document and not any inherited documents
"""Filter for only the instances of this specific document.
Do NOT return any inherited documents.
"""
if self._document._meta.get('allow_inheritance') is True:
self._initial_query = {'_cls': self._document._class_name}

View File

@@ -1857,8 +1857,8 @@ class QuerySetTest(unittest.TestCase):
self.Person.objects()[:1].delete()
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()
del_result = p1.delete(w=0)
self.assertEqual(None, del_result)