diff --git a/docs/guide/defining-documents.rst b/docs/guide/defining-documents.rst
index 82388d3d..bd2b43e2 100644
--- a/docs/guide/defining-documents.rst
+++ b/docs/guide/defining-documents.rst
@@ -744,7 +744,7 @@ Document inheritance
To create a specialised type of a :class:`~mongoengine.Document` you have
defined, you may subclass it and add any extra fields or methods you may need.
-As this is new class is not a direct subclass of
+As this new class is not a direct subclass of
:class:`~mongoengine.Document`, it will not be stored in its own collection; it
will use the same collection as its superclass uses. This allows for more
convenient and efficient retrieval of related documents -- all you need do is
@@ -767,6 +767,27 @@ document.::
Setting :attr:`allow_inheritance` to True should also be used in
:class:`~mongoengine.EmbeddedDocument` class in case you need to subclass it
+When it comes to querying using :attr:`.objects()`, querying `Page.objects()` will query
+both `Page` and `DatedPage` whereas querying `DatedPage` will only query the `DatedPage` documents.
+Behind the scenes, MongoEngine deals with inheritance by adding a :attr:`_cls` attribute that contains
+the class name in every documents. When a document is loaded, MongoEngine checks
+it's :attr:`_cls` attribute and use that class to construct the instance.::
+
+ Page(title='a funky title').save()
+ DatedPage(title='another title', date=datetime.utcnow()).save()
+
+ print(Page.objects().count()) # 2
+ print(DatedPage.objects().count()) # 1
+
+ # print documents in their native form
+ # we remove 'id' to avoid polluting the output with unnecessary detail
+ qs = Page.objects.exclude('id').as_pymongo()
+ print(list(qs))
+ # [
+ # {'_cls': u 'Page', 'title': 'a funky title'},
+ # {'_cls': u 'Page.DatedPage', 'title': u 'another title', 'date': datetime.datetime(2019, 12, 13, 20, 16, 59, 993000)}
+ # ]
+
Working with existing data
--------------------------
As MongoEngine no longer defaults to needing :attr:`_cls`, you can quickly and
diff --git a/docs/guide/mongomock.rst b/docs/guide/mongomock.rst
index d70ee6a6..141d7b69 100644
--- a/docs/guide/mongomock.rst
+++ b/docs/guide/mongomock.rst
@@ -2,10 +2,10 @@
Use mongomock for testing
==============================
-`mongomock `_ is a package to do just
+`mongomock `_ is a package to do just
what the name implies, mocking a mongo database.
-To use with mongoengine, simply specify mongomock when connecting with
+To use with mongoengine, simply specify mongomock when connecting with
mongoengine:
.. code-block:: python
@@ -21,7 +21,7 @@ or with an alias:
conn = get_connection('testdb')
Example of test file:
---------
+---------------------
.. code-block:: python
import unittest
@@ -45,4 +45,4 @@ Example of test file:
pers.save()
fresh_pers = Person.objects().first()
- self.assertEqual(fresh_pers.name, 'John')
+ assert fresh_pers.name == 'John'
diff --git a/docs/guide/querying.rst b/docs/guide/querying.rst
index d64c169c..121325ae 100644
--- a/docs/guide/querying.rst
+++ b/docs/guide/querying.rst
@@ -222,6 +222,18 @@ keyword argument::
.. versionadded:: 0.4
+Sorting/Ordering results
+========================
+It is possible to order the results by 1 or more keys using :meth:`~mongoengine.queryset.QuerySet.order_by`.
+The order may be specified by prepending each of the keys by "+" or "-". Ascending order is assumed if there's no prefix.::
+
+ # Order by ascending date
+ blogs = BlogPost.objects().order_by('date') # equivalent to .order_by('+date')
+
+ # Order by ascending date first, then descending title
+ blogs = BlogPost.objects().order_by('+date', '-title')
+
+
Limiting and skipping results
=============================
Just as with traditional ORMs, you may limit the number of results returned or
@@ -585,7 +597,8 @@ cannot use the `$` syntax in keyword arguments it has been mapped to `S`::
['database', 'mongodb']
From MongoDB version 2.6, push operator supports $position value which allows
-to push values with index.
+to push values with index::
+
>>> post = BlogPost(title="Test", tags=["mongo"])
>>> post.save()
>>> post.update(push__tags__0=["database", "code"])
diff --git a/mongoengine/base/datastructures.py b/mongoengine/base/datastructures.py
index d1b5ae76..dcc1f092 100644
--- a/mongoengine/base/datastructures.py
+++ b/mongoengine/base/datastructures.py
@@ -120,6 +120,9 @@ class BaseList(list):
super(BaseList, self).__init__(list_items)
def __getitem__(self, key):
+ # change index to positive value because MongoDB does not support negative one
+ if isinstance(key, int) and key < 0:
+ key = len(self) + key
value = super(BaseList, self).__getitem__(key)
if isinstance(key, slice):
diff --git a/tests/document/test_instance.py b/tests/document/test_instance.py
index b899684f..a5c21323 100644
--- a/tests/document/test_instance.py
+++ b/tests/document/test_instance.py
@@ -41,7 +41,7 @@ from tests.utils import MongoDBTestCase, get_as_pymongo
TEST_IMAGE_PATH = os.path.join(os.path.dirname(__file__), "../fields/mongoengine.png")
-class TestInstance(MongoDBTestCase):
+class TestDocumentInstance(MongoDBTestCase):
def setUp(self):
class Job(EmbeddedDocument):
name = StringField()
@@ -3338,19 +3338,19 @@ class TestInstance(MongoDBTestCase):
# worker1.job should be equal to the job used originally to create the
# document.
- self.assertEqual(worker1.job, worker.job)
+ assert worker1.job == worker.job
# worker1.job should be equal to a newly created Job EmbeddedDocument
# using either the Boss object or his ID.
- self.assertEqual(worker1.job, Job(boss=boss, boss_dbref=boss))
- self.assertEqual(worker1.job, Job(boss=boss.id, boss_dbref=boss.id))
+ assert worker1.job == Job(boss=boss, boss_dbref=boss)
+ assert worker1.job == Job(boss=boss.id, boss_dbref=boss.id)
# The above equalities should also hold after worker1.job.boss has been
# fetch()ed.
worker1.job.boss.fetch()
- self.assertEqual(worker1.job, worker.job)
- self.assertEqual(worker1.job, Job(boss=boss, boss_dbref=boss))
- self.assertEqual(worker1.job, Job(boss=boss.id, boss_dbref=boss.id))
+ assert worker1.job == worker.job
+ assert worker1.job == Job(boss=boss, boss_dbref=boss)
+ assert worker1.job == Job(boss=boss.id, boss_dbref=boss.id)
def test_dbref_equality(self):
class Test2(Document):
@@ -3617,6 +3617,51 @@ class TestInstance(MongoDBTestCase):
assert b._instance == a
assert idx == 2
+ def test_updating_listfield_manipulate_list(self):
+ class Company(Document):
+ name = StringField()
+ employees = ListField(field=DictField())
+
+ Company.drop_collection()
+
+ comp = Company(name="BigBank", employees=[{"name": "John"}])
+ comp.save()
+ comp.employees.append({"name": "Bill"})
+ comp.save()
+
+ stored_comp = get_as_pymongo(comp)
+ self.assertEqual(
+ stored_comp,
+ {
+ "_id": comp.id,
+ "employees": [{"name": "John"}, {"name": "Bill"}],
+ "name": "BigBank",
+ },
+ )
+
+ comp = comp.reload()
+ comp.employees[0]["color"] = "red"
+ comp.employees[-1]["color"] = "blue"
+ comp.employees[-1].update({"size": "xl"})
+ comp.save()
+
+ assert len(comp.employees) == 2
+ assert comp.employees[0] == {"name": "John", "color": "red"}
+ assert comp.employees[1] == {"name": "Bill", "size": "xl", "color": "blue"}
+
+ stored_comp = get_as_pymongo(comp)
+ self.assertEqual(
+ stored_comp,
+ {
+ "_id": comp.id,
+ "employees": [
+ {"name": "John", "color": "red"},
+ {"size": "xl", "color": "blue", "name": "Bill"},
+ ],
+ "name": "BigBank",
+ },
+ )
+
def test_falsey_pk(self):
"""Ensure that we can create and update a document with Falsey PK."""
@@ -3693,13 +3738,13 @@ class TestInstance(MongoDBTestCase):
value = u"I_should_be_a_dict"
coll.insert_one({"light_saber": value})
- with self.assertRaises(InvalidDocumentError) as cm:
+ with pytest.raises(InvalidDocumentError) as exc_info:
list(Jedi.objects)
- self.assertEqual(
- str(cm.exception),
- "Invalid data to create a `Jedi` instance.\nField 'light_saber' - The source SON object needs to be of type 'dict' but a '%s' was found"
- % type(value),
+ assert str(
+ exc_info.value
+ ) == "Invalid data to create a `Jedi` instance.\nField 'light_saber' - The source SON object needs to be of type 'dict' but a '%s' was found" % type(
+ value
)
diff --git a/tests/fields/test_file_field.py b/tests/fields/test_file_field.py
index bfc86511..b8ece1a9 100644
--- a/tests/fields/test_file_field.py
+++ b/tests/fields/test_file_field.py
@@ -151,7 +151,7 @@ class TestFileField(MongoDBTestCase):
result = StreamFile.objects.first()
assert streamfile == result
assert result.the_file.read() == text + more_text
- # self.assertEqual(result.the_file.content_type, content_type)
+ # assert result.the_file.content_type == content_type
result.the_file.seek(0)
assert result.the_file.tell() == 0
assert result.the_file.read(len(text)) == text