Merge branch 'master' of github.com:MongoEngine/mongoengine into fix_dictfield_validation

This commit is contained in:
Bastien Gérard 2020-03-16 22:30:48 +01:00
commit 87512246cb
12 changed files with 108 additions and 20 deletions

View File

@ -3,7 +3,7 @@
# with a very large number of jobs, hence we only test a subset of all the
# combinations:
# * MongoDB v3.4 & the latest PyMongo v3.x is currently the "main" setup,
# tested against Python v2.7, v3.5, v3.6, v3.7, v3.8 and PyPy.
# tested against Python v2.7, v3.5, v3.6, v3.7, v3.8, PyPy and PyPy3.
# * Besides that, we test the lowest actively supported Python/MongoDB/PyMongo
# combination: MongoDB v3.4, PyMongo v3.4, Python v2.7.
# * MongoDB v3.6 is tested against Python v3.6, and PyMongo v3.6, v3.7, v3.8.
@ -33,15 +33,16 @@ env:
global:
- MONGODB_3_4=3.4.17
- MONGODB_3_6=3.6.12
- PYMONGO_3_10=3.10
- PYMONGO_3_9=3.9
- PYMONGO_3_6=3.6
- MONGODB_4_0=4.0.13
- PYMONGO_3_4=3.4
- PYMONGO_3_6=3.6
- PYMONGO_3_9=3.9
- PYMONGO_3_10=3.10
matrix:
- MONGODB=${MONGODB_3_4} PYMONGO=${PYMONGO_3_10}
matrix:
# Finish the build as soon as one job fails
fast_finish: true
@ -54,6 +55,8 @@ matrix:
env: MONGODB=${MONGODB_3_6} PYMONGO=${PYMONGO_3_9}
- python: 3.7
env: MONGODB=${MONGODB_3_6} PYMONGO=${PYMONGO_3_10}
- python: 3.8
env: MONGODB=${MONGODB_4_0} PYMONGO=${PYMONGO_3_10}
install:
# Install Mongo

View File

@ -255,3 +255,4 @@ that much better:
* Filip Kucharczyk (https://github.com/Pacu2)
* Eric Timmons (https://github.com/daewok)
* Matthew Simpson (https://github.com/mcsimps2)
* Leonardo Domingues (https://github.com/leodmgs)

View File

@ -26,10 +26,10 @@ an `API reference <https://mongoengine-odm.readthedocs.io/apireference.html>`_.
Supported MongoDB Versions
==========================
MongoEngine is currently tested against MongoDB v3.4 and v3.6. Future versions
MongoEngine is currently tested against MongoDB v3.4, v3.6 and v4.0. Future versions
should be supported as well, but aren't actively tested at the moment. Make
sure to open an issue or submit a pull request if you experience any problems
with MongoDB version > 3.6.
with MongoDB version > 4.0.
Installation
============

View File

@ -6,6 +6,9 @@ Changelog
Development
===========
- (Fill this out as you fix issues and develop your features).
- Add Mongo 4.0 to Travis
- Fixed a bug causing inaccurate query results, while combining ``__raw__`` and regular filters for the same field #2264
- Add support for the `elemMatch` projection operator in .fields (e.g BlogPost.objects.fields(elemMatch__comments="test")) #2267
Changes in 0.19.2
=================

View File

@ -56,7 +56,7 @@ class InvalidCollectionError(Exception):
class EmbeddedDocument(six.with_metaclass(DocumentMetaclass, BaseDocument)):
"""A :class:`~mongoengine.Document` that isn't stored in its own
r"""A :class:`~mongoengine.Document` that isn't stored in its own
collection. :class:`~mongoengine.EmbeddedDocument`\ s should be used as
fields on :class:`~mongoengine.Document`\ s through the
:class:`~mongoengine.EmbeddedDocumentField` field type.
@ -332,7 +332,7 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
):
"""Save the :class:`~mongoengine.Document` to the database. If the
document already exists, it will be updated, otherwise it will be
created.
created. Returns the saved object instance.
:param force_insert: only try to create a new document, don't allow
updates of existing documents.

View File

@ -694,8 +694,8 @@ class BaseQuerySet(object):
def in_bulk(self, object_ids):
"""Retrieve a set of documents by their ids.
:param object_ids: a list or tuple of ``ObjectId``\ s
:rtype: dict of ObjectIds as keys and collection-specific
:param object_ids: a list or tuple of ObjectId's
:rtype: dict of ObjectId's as keys and collection-specific
Document subclasses as values.
.. versionadded:: 0.3
@ -1026,9 +1026,11 @@ class BaseQuerySet(object):
posts = BlogPost.objects(...).fields(comments=0)
To retrieve a subrange of array elements:
To retrieve a subrange or sublist of array elements,
support exist for both the `slice` and `elemMatch` projection operator:
posts = BlogPost.objects(...).fields(slice__comments=5)
posts = BlogPost.objects(...).fields(elemMatch__comments="test")
:param kwargs: A set of keyword arguments identifying what to
include, exclude, or slice.
@ -1037,7 +1039,7 @@ class BaseQuerySet(object):
"""
# Check for an operator and transform to mongo-style if there is
operators = ["slice"]
operators = ["slice", "elemMatch"]
cleaned_fields = []
for key, value in kwargs.items():
parts = key.split("__")
@ -1140,7 +1142,7 @@ class BaseQuerySet(object):
def explain(self):
"""Return an explain plan record for the
:class:`~mongoengine.queryset.QuerySet`\ 's cursor.
:class:`~mongoengine.queryset.QuerySet` cursor.
"""
return self._cursor.explain()

View File

@ -169,9 +169,9 @@ def query(_doc_cls=None, **kwargs):
key = ".".join(parts)
if op is None or key not in mongo_query:
if key not in mongo_query:
mongo_query[key] = value
elif key in mongo_query:
else:
if isinstance(mongo_query[key], dict) and isinstance(value, dict):
mongo_query[key].update(value)
# $max/minDistance needs to come last - convert to SON

View File

@ -108,6 +108,10 @@ CLASSIFIERS = [
"Topic :: Software Development :: Libraries :: Python Modules",
]
PYTHON_VERSION = sys.version_info[0]
PY3 = PYTHON_VERSION == 3
PY2 = PYTHON_VERSION == 2
extra_opts = {
"packages": find_packages(exclude=["tests", "tests.*"]),
"tests_require": [
@ -116,9 +120,10 @@ extra_opts = {
"coverage<5.0", # recent coverage switched to sqlite format for the .coverage file which isn't handled properly by coveralls
"blinker",
"Pillow>=2.0.0, <7.0.0", # 7.0.0 dropped Python2 support
"zipp<2.0.0", # (dependency of pytest) dropped python2 support
],
}
if sys.version_info[0] == 3:
if PY3:
extra_opts["use_2to3"] = True
if "test" in sys.argv:
extra_opts["packages"] = find_packages()

View File

@ -65,7 +65,7 @@ class ComplexDateTimeFieldTest(MongoDBTestCase):
for values in itertools.product([2014], mm, dd, hh, ii, ss, microsecond):
stored = LogEntry(date=datetime.datetime(*values)).to_mongo()["date"]
assert (
re.match("^\d{4},\d{2},\d{2},\d{2},\d{2},\d{2},\d{6}$", stored)
re.match(r"^\d{4},\d{2},\d{2},\d{2},\d{2},\d{2},\d{6}$", stored)
is not None
)
@ -74,7 +74,7 @@ class ComplexDateTimeFieldTest(MongoDBTestCase):
"date_with_dots"
]
assert (
re.match("^\d{4}.\d{2}.\d{2}.\d{2}.\d{2}.\d{2}.\d{6}$", stored) is not None
re.match(r"^\d{4}.\d{2}.\d{2}.\d{2}.\d{2}.\d{2}.\d{6}$", stored) is not None
)
def test_complexdatetime_usage(self):

View File

@ -4476,6 +4476,74 @@ class TestQueryset(unittest.TestCase):
expected = "[u'A1', u'A2']"
assert expected == "%s" % sorted(names)
def test_fields(self):
class Bar(EmbeddedDocument):
v = StringField()
z = StringField()
class Foo(Document):
x = StringField()
y = IntField()
items = EmbeddedDocumentListField(Bar)
Foo.drop_collection()
Foo(x="foo1", y=1).save()
Foo(x="foo2", y=2, items=[]).save()
Foo(x="foo3", y=3, items=[Bar(z="a", v="V")]).save()
Foo(
x="foo4",
y=4,
items=[
Bar(z="a", v="V"),
Bar(z="b", v="W"),
Bar(z="b", v="X"),
Bar(z="c", v="V"),
],
).save()
Foo(
x="foo5",
y=5,
items=[
Bar(z="b", v="X"),
Bar(z="c", v="V"),
Bar(z="d", v="V"),
Bar(z="e", v="V"),
],
).save()
foos_with_x = list(Foo.objects.order_by("y").fields(x=1))
assert all(o.x is not None for o in foos_with_x)
foos_without_y = list(Foo.objects.order_by("y").fields(y=0))
assert all(o.y is None for o in foos_with_x)
foos_with_sliced_items = list(Foo.objects.order_by("y").fields(slice__items=1))
assert foos_with_sliced_items[0].items == []
assert foos_with_sliced_items[1].items == []
assert len(foos_with_sliced_items[2].items) == 1
assert foos_with_sliced_items[2].items[0].z == "a"
assert len(foos_with_sliced_items[3].items) == 1
assert foos_with_sliced_items[3].items[0].z == "a"
assert len(foos_with_sliced_items[4].items) == 1
assert foos_with_sliced_items[4].items[0].z == "b"
foos_with_elem_match_items = list(
Foo.objects.order_by("y").fields(elemMatch__items={"z": "b"})
)
assert foos_with_elem_match_items[0].items == []
assert foos_with_elem_match_items[1].items == []
assert foos_with_elem_match_items[2].items == []
assert len(foos_with_elem_match_items[3].items) == 1
assert foos_with_elem_match_items[3].items[0].z == "b"
assert foos_with_elem_match_items[3].items[0].v == "W"
assert len(foos_with_elem_match_items[4].items) == 1
assert foos_with_elem_match_items[4].items[0].z == "b"
def test_elem_match(self):
class Foo(EmbeddedDocument):
shape = StringField()

View File

@ -24,6 +24,12 @@ class TestTransform(unittest.TestCase):
}
assert transform.query(friend__age__gte=30) == {"friend.age": {"$gte": 30}}
assert transform.query(name__exists=True) == {"name": {"$exists": True}}
assert transform.query(name=["Mark"], __raw__={"name": {"$in": "Tom"}}) == {
"$and": [{"name": ["Mark"]}, {"name": {"$in": "Tom"}}]
}
assert transform.query(name__in=["Tom"], __raw__={"name": "Mark"}) == {
"$and": [{"name": {"$in": ["Tom"]}}, {"name": "Mark"}]
}
def test_transform_update(self):
class LisDoc(Document):

View File

@ -1,5 +1,5 @@
[tox]
envlist = {py27,py35,pypy,pypy3}-{mg34,mg36}
envlist = {py27,py35,pypy,pypy3}-{mg34,mg36,mg39,mg310}
[testenv]
commands =