Merge branch 'master' of github.com:MongoEngine/mongoengine into release_0_19_0
This commit is contained in:
commit
c22eb34017
@ -744,7 +744,7 @@ Document inheritance
|
|||||||
|
|
||||||
To create a specialised type of a :class:`~mongoengine.Document` you have
|
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.
|
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
|
: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
|
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
|
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
|
Setting :attr:`allow_inheritance` to True should also be used in
|
||||||
:class:`~mongoengine.EmbeddedDocument` class in case you need to subclass it
|
: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
|
Working with existing data
|
||||||
--------------------------
|
--------------------------
|
||||||
As MongoEngine no longer defaults to needing :attr:`_cls`, you can quickly and
|
As MongoEngine no longer defaults to needing :attr:`_cls`, you can quickly and
|
||||||
|
@ -21,7 +21,7 @@ or with an alias:
|
|||||||
conn = get_connection('testdb')
|
conn = get_connection('testdb')
|
||||||
|
|
||||||
Example of test file:
|
Example of test file:
|
||||||
--------
|
---------------------
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
@ -45,4 +45,4 @@ Example of test file:
|
|||||||
pers.save()
|
pers.save()
|
||||||
|
|
||||||
fresh_pers = Person.objects().first()
|
fresh_pers = Person.objects().first()
|
||||||
self.assertEqual(fresh_pers.name, 'John')
|
assert fresh_pers.name == 'John'
|
||||||
|
@ -222,6 +222,18 @@ keyword argument::
|
|||||||
|
|
||||||
.. versionadded:: 0.4
|
.. 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
|
Limiting and skipping results
|
||||||
=============================
|
=============================
|
||||||
Just as with traditional ORMs, you may limit the number of results returned or
|
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']
|
['database', 'mongodb']
|
||||||
|
|
||||||
From MongoDB version 2.6, push operator supports $position value which allows
|
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 = BlogPost(title="Test", tags=["mongo"])
|
||||||
>>> post.save()
|
>>> post.save()
|
||||||
>>> post.update(push__tags__0=["database", "code"])
|
>>> post.update(push__tags__0=["database", "code"])
|
||||||
|
@ -120,6 +120,9 @@ class BaseList(list):
|
|||||||
super(BaseList, self).__init__(list_items)
|
super(BaseList, self).__init__(list_items)
|
||||||
|
|
||||||
def __getitem__(self, key):
|
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)
|
value = super(BaseList, self).__getitem__(key)
|
||||||
|
|
||||||
if isinstance(key, slice):
|
if isinstance(key, slice):
|
||||||
|
@ -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")
|
TEST_IMAGE_PATH = os.path.join(os.path.dirname(__file__), "../fields/mongoengine.png")
|
||||||
|
|
||||||
|
|
||||||
class TestInstance(MongoDBTestCase):
|
class TestDocumentInstance(MongoDBTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
class Job(EmbeddedDocument):
|
class Job(EmbeddedDocument):
|
||||||
name = StringField()
|
name = StringField()
|
||||||
@ -3338,19 +3338,19 @@ class TestInstance(MongoDBTestCase):
|
|||||||
|
|
||||||
# worker1.job should be equal to the job used originally to create the
|
# worker1.job should be equal to the job used originally to create the
|
||||||
# document.
|
# document.
|
||||||
self.assertEqual(worker1.job, worker.job)
|
assert worker1.job == worker.job
|
||||||
|
|
||||||
# worker1.job should be equal to a newly created Job EmbeddedDocument
|
# worker1.job should be equal to a newly created Job EmbeddedDocument
|
||||||
# using either the Boss object or his ID.
|
# using either the Boss object or his ID.
|
||||||
self.assertEqual(worker1.job, Job(boss=boss, boss_dbref=boss))
|
assert 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.id, boss_dbref=boss.id)
|
||||||
|
|
||||||
# The above equalities should also hold after worker1.job.boss has been
|
# The above equalities should also hold after worker1.job.boss has been
|
||||||
# fetch()ed.
|
# fetch()ed.
|
||||||
worker1.job.boss.fetch()
|
worker1.job.boss.fetch()
|
||||||
self.assertEqual(worker1.job, worker.job)
|
assert worker1.job == worker.job
|
||||||
self.assertEqual(worker1.job, Job(boss=boss, boss_dbref=boss))
|
assert 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.id, boss_dbref=boss.id)
|
||||||
|
|
||||||
def test_dbref_equality(self):
|
def test_dbref_equality(self):
|
||||||
class Test2(Document):
|
class Test2(Document):
|
||||||
@ -3617,6 +3617,51 @@ class TestInstance(MongoDBTestCase):
|
|||||||
assert b._instance == a
|
assert b._instance == a
|
||||||
assert idx == 2
|
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):
|
def test_falsey_pk(self):
|
||||||
"""Ensure that we can create and update a document with Falsey PK."""
|
"""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"
|
value = u"I_should_be_a_dict"
|
||||||
coll.insert_one({"light_saber": value})
|
coll.insert_one({"light_saber": value})
|
||||||
|
|
||||||
with self.assertRaises(InvalidDocumentError) as cm:
|
with pytest.raises(InvalidDocumentError) as exc_info:
|
||||||
list(Jedi.objects)
|
list(Jedi.objects)
|
||||||
|
|
||||||
self.assertEqual(
|
assert str(
|
||||||
str(cm.exception),
|
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"
|
) == "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(
|
||||||
% type(value),
|
value
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -151,7 +151,7 @@ class TestFileField(MongoDBTestCase):
|
|||||||
result = StreamFile.objects.first()
|
result = StreamFile.objects.first()
|
||||||
assert streamfile == result
|
assert streamfile == result
|
||||||
assert result.the_file.read() == text + more_text
|
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)
|
result.the_file.seek(0)
|
||||||
assert result.the_file.tell() == 0
|
assert result.the_file.tell() == 0
|
||||||
assert result.the_file.read(len(text)) == text
|
assert result.the_file.read(len(text)) == text
|
||||||
|
Loading…
x
Reference in New Issue
Block a user