Format the codebase using Black (#2109)
This commit: 1. Formats all of our existing code using `black`. 2. Adds a note about using `black` to `CONTRIBUTING.rst`. 3. Runs `black --check` as part of CI (failing builds that aren't properly formatted).
This commit is contained in:
		@@ -52,19 +52,22 @@ install:
 | 
			
		||||
  - wget http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-${MONGODB}.tgz
 | 
			
		||||
  - tar xzf mongodb-linux-x86_64-${MONGODB}.tgz
 | 
			
		||||
  - ${PWD}/mongodb-linux-x86_64-${MONGODB}/bin/mongod --version
 | 
			
		||||
  # Install python dependencies
 | 
			
		||||
  # Install Python dependencies.
 | 
			
		||||
  - pip install --upgrade pip
 | 
			
		||||
  - pip install coveralls
 | 
			
		||||
  - pip install flake8 flake8-import-order
 | 
			
		||||
  - pip install tox         # tox 3.11.0 has requirement virtualenv>=14.0.0
 | 
			
		||||
  - pip install virtualenv  # virtualenv>=14.0.0 has dropped Python 3.2 support (and pypy3 is based on py32)
 | 
			
		||||
  # Install the tox venv
 | 
			
		||||
  # Install the tox venv.
 | 
			
		||||
  - tox -e $(echo py$TRAVIS_PYTHON_VERSION-mg$PYMONGO | tr -d . | sed -e 's/pypypy/pypy/') -- -e test
 | 
			
		||||
  # Install black for Python v3.7 only.
 | 
			
		||||
  - if [[ $TRAVIS_PYTHON_VERSION == '3.7' ]]; then pip install black; fi
 | 
			
		||||
 | 
			
		||||
before_script:
 | 
			
		||||
  - mkdir ${PWD}/mongodb-linux-x86_64-${MONGODB}/data
 | 
			
		||||
  - ${PWD}/mongodb-linux-x86_64-${MONGODB}/bin/mongod --dbpath ${PWD}/mongodb-linux-x86_64-${MONGODB}/data --logpath ${PWD}/mongodb-linux-x86_64-${MONGODB}/mongodb.log --fork
 | 
			
		||||
  - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then flake8 .; else echo "flake8 only runs on py27"; fi   # Run flake8 for py27
 | 
			
		||||
  - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then flake8 .; else echo "flake8 only runs on py27"; fi   # Run flake8 for Python 2.7 only
 | 
			
		||||
  - if [[ $TRAVIS_PYTHON_VERSION == '3.7' ]]; then black --check .; else echo "black only runs on py37"; fi   # Run black for Python 3.7 only
 | 
			
		||||
  - mongo --eval 'db.version();'    # Make sure mongo is awake
 | 
			
		||||
 | 
			
		||||
script:
 | 
			
		||||
 
 | 
			
		||||
@@ -31,12 +31,8 @@ build. You should ensure that your code is properly converted by
 | 
			
		||||
Style Guide
 | 
			
		||||
-----------
 | 
			
		||||
 | 
			
		||||
MongoEngine aims to follow `PEP8 <http://www.python.org/dev/peps/pep-0008/>`_
 | 
			
		||||
including 4 space indents. When possible we try to stick to 79 character line
 | 
			
		||||
limits. However, screens got bigger and an ORM has a strong focus on
 | 
			
		||||
readability and if it can help, we accept 119 as maximum line length, in a
 | 
			
		||||
similar way as `django does
 | 
			
		||||
<https://docs.djangoproject.com/en/dev/internals/contributing/writing-code/coding-style/#python-style>`_
 | 
			
		||||
MongoEngine uses `black <https://github.com/python/black>`_ for code
 | 
			
		||||
formatting.
 | 
			
		||||
 | 
			
		||||
Testing
 | 
			
		||||
-------
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,18 @@
 | 
			
		||||
from timeit import repeat
 | 
			
		||||
 | 
			
		||||
import mongoengine
 | 
			
		||||
from mongoengine import (BooleanField, Document, EmailField, EmbeddedDocument,
 | 
			
		||||
                         EmbeddedDocumentField, IntField, ListField,
 | 
			
		||||
                         StringField)
 | 
			
		||||
from mongoengine import (
 | 
			
		||||
    BooleanField,
 | 
			
		||||
    Document,
 | 
			
		||||
    EmailField,
 | 
			
		||||
    EmbeddedDocument,
 | 
			
		||||
    EmbeddedDocumentField,
 | 
			
		||||
    IntField,
 | 
			
		||||
    ListField,
 | 
			
		||||
    StringField,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
mongoengine.connect(db='mongoengine_benchmark_test')
 | 
			
		||||
mongoengine.connect(db="mongoengine_benchmark_test")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def timeit(f, n=10000):
 | 
			
		||||
@@ -24,46 +31,41 @@ def test_basic():
 | 
			
		||||
 | 
			
		||||
    def init_book():
 | 
			
		||||
        return Book(
 | 
			
		||||
            name='Always be closing',
 | 
			
		||||
            name="Always be closing",
 | 
			
		||||
            pages=100,
 | 
			
		||||
            tags=['self-help', 'sales'],
 | 
			
		||||
            tags=["self-help", "sales"],
 | 
			
		||||
            is_published=True,
 | 
			
		||||
            author_email='alec@example.com',
 | 
			
		||||
            author_email="alec@example.com",
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    print('Doc initialization: %.3fus' % (timeit(init_book, 1000) * 10**6))
 | 
			
		||||
    print("Doc initialization: %.3fus" % (timeit(init_book, 1000) * 10 ** 6))
 | 
			
		||||
 | 
			
		||||
    b = init_book()
 | 
			
		||||
    print('Doc getattr: %.3fus' % (timeit(lambda: b.name, 10000) * 10**6))
 | 
			
		||||
    print("Doc getattr: %.3fus" % (timeit(lambda: b.name, 10000) * 10 ** 6))
 | 
			
		||||
 | 
			
		||||
    print(
 | 
			
		||||
        'Doc setattr: %.3fus' % (
 | 
			
		||||
            timeit(lambda: setattr(b, 'name', 'New name'), 10000) * 10**6
 | 
			
		||||
        )
 | 
			
		||||
        "Doc setattr: %.3fus"
 | 
			
		||||
        % (timeit(lambda: setattr(b, "name", "New name"), 10000) * 10 ** 6)
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    print('Doc to mongo: %.3fus' % (timeit(b.to_mongo, 1000) * 10**6))
 | 
			
		||||
    print("Doc to mongo: %.3fus" % (timeit(b.to_mongo, 1000) * 10 ** 6))
 | 
			
		||||
 | 
			
		||||
    print('Doc validation: %.3fus' % (timeit(b.validate, 1000) * 10**6))
 | 
			
		||||
    print("Doc validation: %.3fus" % (timeit(b.validate, 1000) * 10 ** 6))
 | 
			
		||||
 | 
			
		||||
    def save_book():
 | 
			
		||||
        b._mark_as_changed('name')
 | 
			
		||||
        b._mark_as_changed('tags')
 | 
			
		||||
        b._mark_as_changed("name")
 | 
			
		||||
        b._mark_as_changed("tags")
 | 
			
		||||
        b.save()
 | 
			
		||||
 | 
			
		||||
    print('Save to database: %.3fus' % (timeit(save_book, 100) * 10**6))
 | 
			
		||||
    print("Save to database: %.3fus" % (timeit(save_book, 100) * 10 ** 6))
 | 
			
		||||
 | 
			
		||||
    son = b.to_mongo()
 | 
			
		||||
    print(
 | 
			
		||||
        'Load from SON: %.3fus' % (
 | 
			
		||||
            timeit(lambda: Book._from_son(son), 1000) * 10**6
 | 
			
		||||
        )
 | 
			
		||||
        "Load from SON: %.3fus" % (timeit(lambda: Book._from_son(son), 1000) * 10 ** 6)
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    print(
 | 
			
		||||
        'Load from database: %.3fus' % (
 | 
			
		||||
            timeit(lambda: Book.objects[0], 100) * 10**6
 | 
			
		||||
        )
 | 
			
		||||
        "Load from database: %.3fus" % (timeit(lambda: Book.objects[0], 100) * 10 ** 6)
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    def create_and_delete_book():
 | 
			
		||||
@@ -72,9 +74,8 @@ def test_basic():
 | 
			
		||||
        b.delete()
 | 
			
		||||
 | 
			
		||||
    print(
 | 
			
		||||
        'Init + save to database + delete: %.3fms' % (
 | 
			
		||||
            timeit(create_and_delete_book, 10) * 10**3
 | 
			
		||||
        )
 | 
			
		||||
        "Init + save to database + delete: %.3fms"
 | 
			
		||||
        % (timeit(create_and_delete_book, 10) * 10 ** 3)
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -92,42 +93,36 @@ def test_big_doc():
 | 
			
		||||
 | 
			
		||||
    def init_company():
 | 
			
		||||
        return Company(
 | 
			
		||||
            name='MongoDB, Inc.',
 | 
			
		||||
            name="MongoDB, Inc.",
 | 
			
		||||
            contacts=[
 | 
			
		||||
                Contact(
 | 
			
		||||
                    name='Contact %d' % x,
 | 
			
		||||
                    title='CEO',
 | 
			
		||||
                    address='Address %d' % x,
 | 
			
		||||
                )
 | 
			
		||||
                Contact(name="Contact %d" % x, title="CEO", address="Address %d" % x)
 | 
			
		||||
                for x in range(1000)
 | 
			
		||||
            ]
 | 
			
		||||
            ],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    company = init_company()
 | 
			
		||||
    print('Big doc to mongo: %.3fms' % (timeit(company.to_mongo, 100) * 10**3))
 | 
			
		||||
    print("Big doc to mongo: %.3fms" % (timeit(company.to_mongo, 100) * 10 ** 3))
 | 
			
		||||
 | 
			
		||||
    print('Big doc validation: %.3fms' % (timeit(company.validate, 1000) * 10**3))
 | 
			
		||||
    print("Big doc validation: %.3fms" % (timeit(company.validate, 1000) * 10 ** 3))
 | 
			
		||||
 | 
			
		||||
    company.save()
 | 
			
		||||
 | 
			
		||||
    def save_company():
 | 
			
		||||
        company._mark_as_changed('name')
 | 
			
		||||
        company._mark_as_changed('contacts')
 | 
			
		||||
        company._mark_as_changed("name")
 | 
			
		||||
        company._mark_as_changed("contacts")
 | 
			
		||||
        company.save()
 | 
			
		||||
 | 
			
		||||
    print('Save to database: %.3fms' % (timeit(save_company, 100) * 10**3))
 | 
			
		||||
    print("Save to database: %.3fms" % (timeit(save_company, 100) * 10 ** 3))
 | 
			
		||||
 | 
			
		||||
    son = company.to_mongo()
 | 
			
		||||
    print(
 | 
			
		||||
        'Load from SON: %.3fms' % (
 | 
			
		||||
            timeit(lambda: Company._from_son(son), 100) * 10**3
 | 
			
		||||
        )
 | 
			
		||||
        "Load from SON: %.3fms"
 | 
			
		||||
        % (timeit(lambda: Company._from_son(son), 100) * 10 ** 3)
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    print(
 | 
			
		||||
        'Load from database: %.3fms' % (
 | 
			
		||||
            timeit(lambda: Company.objects[0], 100) * 10**3
 | 
			
		||||
        )
 | 
			
		||||
        "Load from database: %.3fms"
 | 
			
		||||
        % (timeit(lambda: Company.objects[0], 100) * 10 ** 3)
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    def create_and_delete_company():
 | 
			
		||||
@@ -136,13 +131,12 @@ def test_big_doc():
 | 
			
		||||
        c.delete()
 | 
			
		||||
 | 
			
		||||
    print(
 | 
			
		||||
        'Init + save to database + delete: %.3fms' % (
 | 
			
		||||
            timeit(create_and_delete_company, 10) * 10**3
 | 
			
		||||
        )
 | 
			
		||||
        "Init + save to database + delete: %.3fms"
 | 
			
		||||
        % (timeit(create_and_delete_company, 10) * 10 ** 3)
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    test_basic()
 | 
			
		||||
    print('-' * 100)
 | 
			
		||||
    print("-" * 100)
 | 
			
		||||
    test_big_doc()
 | 
			
		||||
 
 | 
			
		||||
@@ -26,10 +26,10 @@ myNoddys = noddy.find()
 | 
			
		||||
[n for n in myNoddys]  # iterate
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
    print('-' * 100)
 | 
			
		||||
    print('PyMongo: Creating 10000 dictionaries.')
 | 
			
		||||
    print("-" * 100)
 | 
			
		||||
    print("PyMongo: Creating 10000 dictionaries.")
 | 
			
		||||
    t = timeit.Timer(stmt=stmt, setup=setup)
 | 
			
		||||
    print('{}s'.format(t.timeit(1)))
 | 
			
		||||
    print("{}s".format(t.timeit(1)))
 | 
			
		||||
 | 
			
		||||
    stmt = """
 | 
			
		||||
from pymongo import MongoClient, WriteConcern
 | 
			
		||||
@@ -49,10 +49,10 @@ myNoddys = noddy.find()
 | 
			
		||||
[n for n in myNoddys]  # iterate
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
    print('-' * 100)
 | 
			
		||||
    print("-" * 100)
 | 
			
		||||
    print('PyMongo: Creating 10000 dictionaries (write_concern={"w": 0}).')
 | 
			
		||||
    t = timeit.Timer(stmt=stmt, setup=setup)
 | 
			
		||||
    print('{}s'.format(t.timeit(1)))
 | 
			
		||||
    print("{}s".format(t.timeit(1)))
 | 
			
		||||
 | 
			
		||||
    setup = """
 | 
			
		||||
from pymongo import MongoClient
 | 
			
		||||
@@ -78,10 +78,10 @@ myNoddys = Noddy.objects()
 | 
			
		||||
[n for n in myNoddys]  # iterate
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
    print('-' * 100)
 | 
			
		||||
    print('MongoEngine: Creating 10000 dictionaries.')
 | 
			
		||||
    print("-" * 100)
 | 
			
		||||
    print("MongoEngine: Creating 10000 dictionaries.")
 | 
			
		||||
    t = timeit.Timer(stmt=stmt, setup=setup)
 | 
			
		||||
    print('{}s'.format(t.timeit(1)))
 | 
			
		||||
    print("{}s".format(t.timeit(1)))
 | 
			
		||||
 | 
			
		||||
    stmt = """
 | 
			
		||||
for i in range(10000):
 | 
			
		||||
@@ -96,10 +96,10 @@ myNoddys = Noddy.objects()
 | 
			
		||||
[n for n in myNoddys]  # iterate
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
    print('-' * 100)
 | 
			
		||||
    print('MongoEngine: Creating 10000 dictionaries (using a single field assignment).')
 | 
			
		||||
    print("-" * 100)
 | 
			
		||||
    print("MongoEngine: Creating 10000 dictionaries (using a single field assignment).")
 | 
			
		||||
    t = timeit.Timer(stmt=stmt, setup=setup)
 | 
			
		||||
    print('{}s'.format(t.timeit(1)))
 | 
			
		||||
    print("{}s".format(t.timeit(1)))
 | 
			
		||||
 | 
			
		||||
    stmt = """
 | 
			
		||||
for i in range(10000):
 | 
			
		||||
@@ -112,10 +112,10 @@ myNoddys = Noddy.objects()
 | 
			
		||||
[n for n in myNoddys] # iterate
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
    print('-' * 100)
 | 
			
		||||
    print("-" * 100)
 | 
			
		||||
    print('MongoEngine: Creating 10000 dictionaries (write_concern={"w": 0}).')
 | 
			
		||||
    t = timeit.Timer(stmt=stmt, setup=setup)
 | 
			
		||||
    print('{}s'.format(t.timeit(1)))
 | 
			
		||||
    print("{}s".format(t.timeit(1)))
 | 
			
		||||
 | 
			
		||||
    stmt = """
 | 
			
		||||
for i in range(10000):
 | 
			
		||||
@@ -128,10 +128,12 @@ myNoddys = Noddy.objects()
 | 
			
		||||
[n for n in myNoddys] # iterate
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
    print('-' * 100)
 | 
			
		||||
    print('MongoEngine: Creating 10000 dictionaries (write_concern={"w": 0}, validate=False).')
 | 
			
		||||
    print("-" * 100)
 | 
			
		||||
    print(
 | 
			
		||||
        'MongoEngine: Creating 10000 dictionaries (write_concern={"w": 0}, validate=False).'
 | 
			
		||||
    )
 | 
			
		||||
    t = timeit.Timer(stmt=stmt, setup=setup)
 | 
			
		||||
    print('{}s'.format(t.timeit(1)))
 | 
			
		||||
    print("{}s".format(t.timeit(1)))
 | 
			
		||||
 | 
			
		||||
    stmt = """
 | 
			
		||||
for i in range(10000):
 | 
			
		||||
@@ -144,10 +146,12 @@ myNoddys = Noddy.objects()
 | 
			
		||||
[n for n in myNoddys] # iterate
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
    print('-' * 100)
 | 
			
		||||
    print('MongoEngine: Creating 10000 dictionaries (force_insert=True, write_concern={"w": 0}, validate=False).')
 | 
			
		||||
    print("-" * 100)
 | 
			
		||||
    print(
 | 
			
		||||
        'MongoEngine: Creating 10000 dictionaries (force_insert=True, write_concern={"w": 0}, validate=False).'
 | 
			
		||||
    )
 | 
			
		||||
    t = timeit.Timer(stmt=stmt, setup=setup)
 | 
			
		||||
    print('{}s'.format(t.timeit(1)))
 | 
			
		||||
    print("{}s".format(t.timeit(1)))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,19 @@
 | 
			
		||||
from mongoengine import *
 | 
			
		||||
 | 
			
		||||
connect('tumblelog')
 | 
			
		||||
connect("tumblelog")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Comment(EmbeddedDocument):
 | 
			
		||||
    content = StringField()
 | 
			
		||||
    name = StringField(max_length=120)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class User(Document):
 | 
			
		||||
    email = StringField(required=True)
 | 
			
		||||
    first_name = StringField(max_length=50)
 | 
			
		||||
    last_name = StringField(max_length=50)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Post(Document):
 | 
			
		||||
    title = StringField(max_length=120, required=True)
 | 
			
		||||
    author = ReferenceField(User)
 | 
			
		||||
@@ -18,54 +21,57 @@ class Post(Document):
 | 
			
		||||
    comments = ListField(EmbeddedDocumentField(Comment))
 | 
			
		||||
 | 
			
		||||
    # bugfix
 | 
			
		||||
    meta = {'allow_inheritance': True}
 | 
			
		||||
    meta = {"allow_inheritance": True}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TextPost(Post):
 | 
			
		||||
    content = StringField()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ImagePost(Post):
 | 
			
		||||
    image_path = StringField()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LinkPost(Post):
 | 
			
		||||
    link_url = StringField()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Post.drop_collection()
 | 
			
		||||
 | 
			
		||||
john = User(email='jdoe@example.com', first_name='John', last_name='Doe')
 | 
			
		||||
john = User(email="jdoe@example.com", first_name="John", last_name="Doe")
 | 
			
		||||
john.save()
 | 
			
		||||
 | 
			
		||||
post1 = TextPost(title='Fun with MongoEngine', author=john)
 | 
			
		||||
post1.content = 'Took a look at MongoEngine today, looks pretty cool.'
 | 
			
		||||
post1.tags = ['mongodb', 'mongoengine']
 | 
			
		||||
post1 = TextPost(title="Fun with MongoEngine", author=john)
 | 
			
		||||
post1.content = "Took a look at MongoEngine today, looks pretty cool."
 | 
			
		||||
post1.tags = ["mongodb", "mongoengine"]
 | 
			
		||||
post1.save()
 | 
			
		||||
 | 
			
		||||
post2 = LinkPost(title='MongoEngine Documentation', author=john)
 | 
			
		||||
post2.link_url = 'http://tractiondigital.com/labs/mongoengine/docs'
 | 
			
		||||
post2.tags = ['mongoengine']
 | 
			
		||||
post2 = LinkPost(title="MongoEngine Documentation", author=john)
 | 
			
		||||
post2.link_url = "http://tractiondigital.com/labs/mongoengine/docs"
 | 
			
		||||
post2.tags = ["mongoengine"]
 | 
			
		||||
post2.save()
 | 
			
		||||
 | 
			
		||||
print('ALL POSTS')
 | 
			
		||||
print("ALL POSTS")
 | 
			
		||||
print()
 | 
			
		||||
for post in Post.objects:
 | 
			
		||||
    print(post.title)
 | 
			
		||||
    #print '=' * post.title.count()
 | 
			
		||||
    # print '=' * post.title.count()
 | 
			
		||||
    print("=" * 20)
 | 
			
		||||
 | 
			
		||||
    if isinstance(post, TextPost):
 | 
			
		||||
        print(post.content)
 | 
			
		||||
 | 
			
		||||
    if isinstance(post, LinkPost):
 | 
			
		||||
        print('Link:', post.link_url)
 | 
			
		||||
        print("Link:", post.link_url)
 | 
			
		||||
 | 
			
		||||
    print()
 | 
			
		||||
print()
 | 
			
		||||
 | 
			
		||||
print('POSTS TAGGED \'MONGODB\'')
 | 
			
		||||
print("POSTS TAGGED 'MONGODB'")
 | 
			
		||||
print()
 | 
			
		||||
for post in Post.objects(tags='mongodb'):
 | 
			
		||||
for post in Post.objects(tags="mongodb"):
 | 
			
		||||
    print(post.title)
 | 
			
		||||
print()
 | 
			
		||||
 | 
			
		||||
num_posts = Post.objects(tags='mongodb').count()
 | 
			
		||||
num_posts = Post.objects(tags="mongodb").count()
 | 
			
		||||
print('Found %d posts with tag "mongodb"' % num_posts)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										93
									
								
								docs/conf.py
									
									
									
									
									
								
							
							
						
						
									
										93
									
								
								docs/conf.py
									
									
									
									
									
								
							@@ -20,29 +20,29 @@ import mongoengine
 | 
			
		||||
# If extensions (or modules to document with autodoc) are in another directory,
 | 
			
		||||
# add these directories to sys.path here. If the directory is relative to the
 | 
			
		||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
 | 
			
		||||
sys.path.insert(0, os.path.abspath('..'))
 | 
			
		||||
sys.path.insert(0, os.path.abspath(".."))
 | 
			
		||||
 | 
			
		||||
# -- General configuration -----------------------------------------------------
 | 
			
		||||
 | 
			
		||||
# Add any Sphinx extension module names here, as strings. They can be extensions
 | 
			
		||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
 | 
			
		||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo']
 | 
			
		||||
extensions = ["sphinx.ext.autodoc", "sphinx.ext.todo"]
 | 
			
		||||
 | 
			
		||||
# Add any paths that contain templates here, relative to this directory.
 | 
			
		||||
templates_path = ['_templates']
 | 
			
		||||
templates_path = ["_templates"]
 | 
			
		||||
 | 
			
		||||
# The suffix of source filenames.
 | 
			
		||||
source_suffix = '.rst'
 | 
			
		||||
source_suffix = ".rst"
 | 
			
		||||
 | 
			
		||||
# The encoding of source files.
 | 
			
		||||
#source_encoding = 'utf-8'
 | 
			
		||||
# source_encoding = 'utf-8'
 | 
			
		||||
 | 
			
		||||
# The master toctree document.
 | 
			
		||||
master_doc = 'index'
 | 
			
		||||
master_doc = "index"
 | 
			
		||||
 | 
			
		||||
# General information about the project.
 | 
			
		||||
project = u'MongoEngine'
 | 
			
		||||
copyright = u'2009, MongoEngine Authors'
 | 
			
		||||
project = u"MongoEngine"
 | 
			
		||||
copyright = u"2009, MongoEngine Authors"
 | 
			
		||||
 | 
			
		||||
# The version info for the project you're documenting, acts as replacement for
 | 
			
		||||
# |version| and |release|, also used in various other places throughout the
 | 
			
		||||
@@ -55,68 +55,66 @@ release = mongoengine.get_version()
 | 
			
		||||
 | 
			
		||||
# The language for content autogenerated by Sphinx. Refer to documentation
 | 
			
		||||
# for a list of supported languages.
 | 
			
		||||
#language = None
 | 
			
		||||
# language = None
 | 
			
		||||
 | 
			
		||||
# There are two options for replacing |today|: either, you set today to some
 | 
			
		||||
# non-false value, then it is used:
 | 
			
		||||
#today = ''
 | 
			
		||||
# today = ''
 | 
			
		||||
# Else, today_fmt is used as the format for a strftime call.
 | 
			
		||||
#today_fmt = '%B %d, %Y'
 | 
			
		||||
# today_fmt = '%B %d, %Y'
 | 
			
		||||
 | 
			
		||||
# List of documents that shouldn't be included in the build.
 | 
			
		||||
#unused_docs = []
 | 
			
		||||
# unused_docs = []
 | 
			
		||||
 | 
			
		||||
# List of directories, relative to source directory, that shouldn't be searched
 | 
			
		||||
# for source files.
 | 
			
		||||
exclude_trees = ['_build']
 | 
			
		||||
exclude_trees = ["_build"]
 | 
			
		||||
 | 
			
		||||
# The reST default role (used for this markup: `text`) to use for all documents.
 | 
			
		||||
#default_role = None
 | 
			
		||||
# default_role = None
 | 
			
		||||
 | 
			
		||||
# If true, '()' will be appended to :func: etc. cross-reference text.
 | 
			
		||||
#add_function_parentheses = True
 | 
			
		||||
# add_function_parentheses = True
 | 
			
		||||
 | 
			
		||||
# If true, the current module name will be prepended to all description
 | 
			
		||||
# unit titles (such as .. function::).
 | 
			
		||||
#add_module_names = True
 | 
			
		||||
# add_module_names = True
 | 
			
		||||
 | 
			
		||||
# If true, sectionauthor and moduleauthor directives will be shown in the
 | 
			
		||||
# output. They are ignored by default.
 | 
			
		||||
#show_authors = False
 | 
			
		||||
# show_authors = False
 | 
			
		||||
 | 
			
		||||
# The name of the Pygments (syntax highlighting) style to use.
 | 
			
		||||
pygments_style = 'sphinx'
 | 
			
		||||
pygments_style = "sphinx"
 | 
			
		||||
 | 
			
		||||
# A list of ignored prefixes for module index sorting.
 | 
			
		||||
#modindex_common_prefix = []
 | 
			
		||||
# modindex_common_prefix = []
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# -- Options for HTML output ---------------------------------------------------
 | 
			
		||||
 | 
			
		||||
# The theme to use for HTML and HTML Help pages.  Major themes that come with
 | 
			
		||||
# Sphinx are currently 'default' and 'sphinxdoc'.
 | 
			
		||||
html_theme = 'sphinx_rtd_theme'
 | 
			
		||||
html_theme = "sphinx_rtd_theme"
 | 
			
		||||
 | 
			
		||||
# Theme options are theme-specific and customize the look and feel of a theme
 | 
			
		||||
# further.  For a list of options available for each theme, see the
 | 
			
		||||
# documentation.
 | 
			
		||||
html_theme_options = {
 | 
			
		||||
    'canonical_url': 'http://docs.mongoengine.org/en/latest/'
 | 
			
		||||
}
 | 
			
		||||
html_theme_options = {"canonical_url": "http://docs.mongoengine.org/en/latest/"}
 | 
			
		||||
 | 
			
		||||
# Add any paths that contain custom themes here, relative to this directory.
 | 
			
		||||
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
 | 
			
		||||
 | 
			
		||||
# The name for this set of Sphinx documents.  If None, it defaults to
 | 
			
		||||
# "<project> v<release> documentation".
 | 
			
		||||
#html_title = None
 | 
			
		||||
# html_title = None
 | 
			
		||||
 | 
			
		||||
# A shorter title for the navigation bar.  Default is the same as html_title.
 | 
			
		||||
#html_short_title = None
 | 
			
		||||
# html_short_title = None
 | 
			
		||||
 | 
			
		||||
# The name of an image file (relative to this directory) to place at the top
 | 
			
		||||
# of the sidebar.
 | 
			
		||||
#html_logo = None
 | 
			
		||||
# html_logo = None
 | 
			
		||||
 | 
			
		||||
# The name of an image file (within the static path) to use as favicon of the
 | 
			
		||||
# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
 | 
			
		||||
@@ -126,11 +124,11 @@ html_favicon = "favicon.ico"
 | 
			
		||||
# Add any paths that contain custom static files (such as style sheets) here,
 | 
			
		||||
# relative to this directory. They are copied after the builtin static files,
 | 
			
		||||
# so a file named "default.css" will overwrite the builtin "default.css".
 | 
			
		||||
#html_static_path = ['_static']
 | 
			
		||||
# html_static_path = ['_static']
 | 
			
		||||
 | 
			
		||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
 | 
			
		||||
# using the given strftime format.
 | 
			
		||||
#html_last_updated_fmt = '%b %d, %Y'
 | 
			
		||||
# html_last_updated_fmt = '%b %d, %Y'
 | 
			
		||||
 | 
			
		||||
# If true, SmartyPants will be used to convert quotes and dashes to
 | 
			
		||||
# typographically correct entities.
 | 
			
		||||
@@ -138,69 +136,68 @@ html_use_smartypants = True
 | 
			
		||||
 | 
			
		||||
# Custom sidebar templates, maps document names to template names.
 | 
			
		||||
html_sidebars = {
 | 
			
		||||
    'index': ['globaltoc.html', 'searchbox.html'],
 | 
			
		||||
    '**': ['localtoc.html', 'relations.html', 'searchbox.html']
 | 
			
		||||
    "index": ["globaltoc.html", "searchbox.html"],
 | 
			
		||||
    "**": ["localtoc.html", "relations.html", "searchbox.html"],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Additional templates that should be rendered to pages, maps page names to
 | 
			
		||||
# template names.
 | 
			
		||||
#html_additional_pages = {}
 | 
			
		||||
# html_additional_pages = {}
 | 
			
		||||
 | 
			
		||||
# If false, no module index is generated.
 | 
			
		||||
#html_use_modindex = True
 | 
			
		||||
# html_use_modindex = True
 | 
			
		||||
 | 
			
		||||
# If false, no index is generated.
 | 
			
		||||
#html_use_index = True
 | 
			
		||||
# html_use_index = True
 | 
			
		||||
 | 
			
		||||
# If true, the index is split into individual pages for each letter.
 | 
			
		||||
#html_split_index = False
 | 
			
		||||
# html_split_index = False
 | 
			
		||||
 | 
			
		||||
# If true, links to the reST sources are added to the pages.
 | 
			
		||||
#html_show_sourcelink = True
 | 
			
		||||
# html_show_sourcelink = True
 | 
			
		||||
 | 
			
		||||
# If true, an OpenSearch description file will be output, and all pages will
 | 
			
		||||
# contain a <link> tag referring to it.  The value of this option must be the
 | 
			
		||||
# base URL from which the finished HTML is served.
 | 
			
		||||
#html_use_opensearch = ''
 | 
			
		||||
# html_use_opensearch = ''
 | 
			
		||||
 | 
			
		||||
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
 | 
			
		||||
#html_file_suffix = ''
 | 
			
		||||
# html_file_suffix = ''
 | 
			
		||||
 | 
			
		||||
# Output file base name for HTML help builder.
 | 
			
		||||
htmlhelp_basename = 'MongoEnginedoc'
 | 
			
		||||
htmlhelp_basename = "MongoEnginedoc"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# -- Options for LaTeX output --------------------------------------------------
 | 
			
		||||
 | 
			
		||||
# The paper size ('letter' or 'a4').
 | 
			
		||||
latex_paper_size = 'a4'
 | 
			
		||||
latex_paper_size = "a4"
 | 
			
		||||
 | 
			
		||||
# The font size ('10pt', '11pt' or '12pt').
 | 
			
		||||
#latex_font_size = '10pt'
 | 
			
		||||
# latex_font_size = '10pt'
 | 
			
		||||
 | 
			
		||||
# Grouping the document tree into LaTeX files. List of tuples
 | 
			
		||||
# (source start file, target name, title, author, documentclass [howto/manual]).
 | 
			
		||||
latex_documents = [
 | 
			
		||||
  ('index', 'MongoEngine.tex', 'MongoEngine Documentation',
 | 
			
		||||
   'Ross Lawley', 'manual'),
 | 
			
		||||
    ("index", "MongoEngine.tex", "MongoEngine Documentation", "Ross Lawley", "manual")
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
# The name of an image file (relative to this directory) to place at the top of
 | 
			
		||||
# the title page.
 | 
			
		||||
#latex_logo = None
 | 
			
		||||
# latex_logo = None
 | 
			
		||||
 | 
			
		||||
# For "manual" documents, if this is true, then toplevel headings are parts,
 | 
			
		||||
# not chapters.
 | 
			
		||||
#latex_use_parts = False
 | 
			
		||||
# latex_use_parts = False
 | 
			
		||||
 | 
			
		||||
# Additional stuff for the LaTeX preamble.
 | 
			
		||||
#latex_preamble = ''
 | 
			
		||||
# latex_preamble = ''
 | 
			
		||||
 | 
			
		||||
# Documents to append as an appendix to all manuals.
 | 
			
		||||
#latex_appendices = []
 | 
			
		||||
# latex_appendices = []
 | 
			
		||||
 | 
			
		||||
# If false, no module index is generated.
 | 
			
		||||
#latex_use_modindex = True
 | 
			
		||||
# latex_use_modindex = True
 | 
			
		||||
 | 
			
		||||
autoclass_content = 'both'
 | 
			
		||||
autoclass_content = "both"
 | 
			
		||||
 
 | 
			
		||||
@@ -18,9 +18,14 @@ from mongoengine.queryset import *
 | 
			
		||||
from mongoengine.signals import *
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = (list(document.__all__) + list(fields.__all__) +
 | 
			
		||||
           list(connection.__all__) + list(queryset.__all__) +
 | 
			
		||||
           list(signals.__all__) + list(errors.__all__))
 | 
			
		||||
__all__ = (
 | 
			
		||||
    list(document.__all__)
 | 
			
		||||
    + list(fields.__all__)
 | 
			
		||||
    + list(connection.__all__)
 | 
			
		||||
    + list(queryset.__all__)
 | 
			
		||||
    + list(signals.__all__)
 | 
			
		||||
    + list(errors.__all__)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
VERSION = (0, 18, 2)
 | 
			
		||||
@@ -31,7 +36,7 @@ def get_version():
 | 
			
		||||
 | 
			
		||||
    For example, if `VERSION == (0, 10, 7)`, return '0.10.7'.
 | 
			
		||||
    """
 | 
			
		||||
    return '.'.join(map(str, VERSION))
 | 
			
		||||
    return ".".join(map(str, VERSION))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__version__ = get_version()
 | 
			
		||||
 
 | 
			
		||||
@@ -12,17 +12,22 @@ from mongoengine.base.metaclasses import *
 | 
			
		||||
 | 
			
		||||
__all__ = (
 | 
			
		||||
    # common
 | 
			
		||||
    'UPDATE_OPERATORS', '_document_registry', 'get_document',
 | 
			
		||||
 | 
			
		||||
    "UPDATE_OPERATORS",
 | 
			
		||||
    "_document_registry",
 | 
			
		||||
    "get_document",
 | 
			
		||||
    # datastructures
 | 
			
		||||
    'BaseDict', 'BaseList', 'EmbeddedDocumentList', 'LazyReference',
 | 
			
		||||
 | 
			
		||||
    "BaseDict",
 | 
			
		||||
    "BaseList",
 | 
			
		||||
    "EmbeddedDocumentList",
 | 
			
		||||
    "LazyReference",
 | 
			
		||||
    # document
 | 
			
		||||
    'BaseDocument',
 | 
			
		||||
 | 
			
		||||
    "BaseDocument",
 | 
			
		||||
    # fields
 | 
			
		||||
    'BaseField', 'ComplexBaseField', 'ObjectIdField', 'GeoJsonBaseField',
 | 
			
		||||
 | 
			
		||||
    "BaseField",
 | 
			
		||||
    "ComplexBaseField",
 | 
			
		||||
    "ObjectIdField",
 | 
			
		||||
    "GeoJsonBaseField",
 | 
			
		||||
    # metaclasses
 | 
			
		||||
    'DocumentMetaclass', 'TopLevelDocumentMetaclass'
 | 
			
		||||
    "DocumentMetaclass",
 | 
			
		||||
    "TopLevelDocumentMetaclass",
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,25 @@
 | 
			
		||||
from mongoengine.errors import NotRegistered
 | 
			
		||||
 | 
			
		||||
__all__ = ('UPDATE_OPERATORS', 'get_document', '_document_registry')
 | 
			
		||||
__all__ = ("UPDATE_OPERATORS", "get_document", "_document_registry")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
UPDATE_OPERATORS = {'set', 'unset', 'inc', 'dec', 'mul',
 | 
			
		||||
                    'pop', 'push', 'push_all', 'pull',
 | 
			
		||||
                    'pull_all', 'add_to_set', 'set_on_insert',
 | 
			
		||||
                    'min', 'max', 'rename'}
 | 
			
		||||
UPDATE_OPERATORS = {
 | 
			
		||||
    "set",
 | 
			
		||||
    "unset",
 | 
			
		||||
    "inc",
 | 
			
		||||
    "dec",
 | 
			
		||||
    "mul",
 | 
			
		||||
    "pop",
 | 
			
		||||
    "push",
 | 
			
		||||
    "push_all",
 | 
			
		||||
    "pull",
 | 
			
		||||
    "pull_all",
 | 
			
		||||
    "add_to_set",
 | 
			
		||||
    "set_on_insert",
 | 
			
		||||
    "min",
 | 
			
		||||
    "max",
 | 
			
		||||
    "rename",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
_document_registry = {}
 | 
			
		||||
@@ -17,25 +30,33 @@ def get_document(name):
 | 
			
		||||
    doc = _document_registry.get(name, None)
 | 
			
		||||
    if not doc:
 | 
			
		||||
        # Possible old style name
 | 
			
		||||
        single_end = name.split('.')[-1]
 | 
			
		||||
        compound_end = '.%s' % single_end
 | 
			
		||||
        possible_match = [k for k in _document_registry
 | 
			
		||||
                          if k.endswith(compound_end) or k == single_end]
 | 
			
		||||
        single_end = name.split(".")[-1]
 | 
			
		||||
        compound_end = ".%s" % single_end
 | 
			
		||||
        possible_match = [
 | 
			
		||||
            k for k in _document_registry if k.endswith(compound_end) or k == single_end
 | 
			
		||||
        ]
 | 
			
		||||
        if len(possible_match) == 1:
 | 
			
		||||
            doc = _document_registry.get(possible_match.pop(), None)
 | 
			
		||||
    if not doc:
 | 
			
		||||
        raise NotRegistered("""
 | 
			
		||||
        raise NotRegistered(
 | 
			
		||||
            """
 | 
			
		||||
            `%s` has not been registered in the document registry.
 | 
			
		||||
            Importing the document class automatically registers it, has it
 | 
			
		||||
            been imported?
 | 
			
		||||
        """.strip() % name)
 | 
			
		||||
        """.strip()
 | 
			
		||||
            % name
 | 
			
		||||
        )
 | 
			
		||||
    return doc
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _get_documents_by_db(connection_alias, default_connection_alias):
 | 
			
		||||
    """Get all registered Documents class attached to a given database"""
 | 
			
		||||
    def get_doc_alias(doc_cls):
 | 
			
		||||
        return doc_cls._meta.get('db_alias', default_connection_alias)
 | 
			
		||||
 | 
			
		||||
    return [doc_cls for doc_cls in _document_registry.values()
 | 
			
		||||
            if get_doc_alias(doc_cls) == connection_alias]
 | 
			
		||||
    def get_doc_alias(doc_cls):
 | 
			
		||||
        return doc_cls._meta.get("db_alias", default_connection_alias)
 | 
			
		||||
 | 
			
		||||
    return [
 | 
			
		||||
        doc_cls
 | 
			
		||||
        for doc_cls in _document_registry.values()
 | 
			
		||||
        if get_doc_alias(doc_cls) == connection_alias
 | 
			
		||||
    ]
 | 
			
		||||
 
 | 
			
		||||
@@ -7,26 +7,36 @@ from six import iteritems
 | 
			
		||||
from mongoengine.common import _import_class
 | 
			
		||||
from mongoengine.errors import DoesNotExist, MultipleObjectsReturned
 | 
			
		||||
 | 
			
		||||
__all__ = ('BaseDict', 'StrictDict', 'BaseList', 'EmbeddedDocumentList', 'LazyReference')
 | 
			
		||||
__all__ = (
 | 
			
		||||
    "BaseDict",
 | 
			
		||||
    "StrictDict",
 | 
			
		||||
    "BaseList",
 | 
			
		||||
    "EmbeddedDocumentList",
 | 
			
		||||
    "LazyReference",
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def mark_as_changed_wrapper(parent_method):
 | 
			
		||||
    """Decorator that ensures _mark_as_changed method gets called."""
 | 
			
		||||
 | 
			
		||||
    def wrapper(self, *args, **kwargs):
 | 
			
		||||
        # 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):
 | 
			
		||||
    """Decorator that ensures _mark_as_changed method gets called with the key argument"""
 | 
			
		||||
 | 
			
		||||
    def wrapper(self, key, *args, **kwargs):
 | 
			
		||||
        # Can't use super() in the decorator.
 | 
			
		||||
        result = parent_method(self, key, *args, **kwargs)
 | 
			
		||||
        self._mark_as_changed(key)
 | 
			
		||||
        return result
 | 
			
		||||
 | 
			
		||||
    return wrapper
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -38,7 +48,7 @@ class BaseDict(dict):
 | 
			
		||||
    _name = None
 | 
			
		||||
 | 
			
		||||
    def __init__(self, dict_items, instance, name):
 | 
			
		||||
        BaseDocument = _import_class('BaseDocument')
 | 
			
		||||
        BaseDocument = _import_class("BaseDocument")
 | 
			
		||||
 | 
			
		||||
        if isinstance(instance, BaseDocument):
 | 
			
		||||
            self._instance = weakref.proxy(instance)
 | 
			
		||||
@@ -55,15 +65,15 @@ class BaseDict(dict):
 | 
			
		||||
    def __getitem__(self, key):
 | 
			
		||||
        value = super(BaseDict, self).__getitem__(key)
 | 
			
		||||
 | 
			
		||||
        EmbeddedDocument = _import_class('EmbeddedDocument')
 | 
			
		||||
        EmbeddedDocument = _import_class("EmbeddedDocument")
 | 
			
		||||
        if isinstance(value, EmbeddedDocument) and value._instance is None:
 | 
			
		||||
            value._instance = self._instance
 | 
			
		||||
        elif isinstance(value, dict) and not isinstance(value, BaseDict):
 | 
			
		||||
            value = BaseDict(value, None, '%s.%s' % (self._name, key))
 | 
			
		||||
            value = BaseDict(value, None, "%s.%s" % (self._name, key))
 | 
			
		||||
            super(BaseDict, self).__setitem__(key, value)
 | 
			
		||||
            value._instance = self._instance
 | 
			
		||||
        elif isinstance(value, list) and not isinstance(value, BaseList):
 | 
			
		||||
            value = BaseList(value, None, '%s.%s' % (self._name, key))
 | 
			
		||||
            value = BaseList(value, None, "%s.%s" % (self._name, key))
 | 
			
		||||
            super(BaseDict, self).__setitem__(key, value)
 | 
			
		||||
            value._instance = self._instance
 | 
			
		||||
        return value
 | 
			
		||||
@@ -87,9 +97,9 @@ class BaseDict(dict):
 | 
			
		||||
    setdefault = mark_as_changed_wrapper(dict.setdefault)
 | 
			
		||||
 | 
			
		||||
    def _mark_as_changed(self, key=None):
 | 
			
		||||
        if hasattr(self._instance, '_mark_as_changed'):
 | 
			
		||||
        if hasattr(self._instance, "_mark_as_changed"):
 | 
			
		||||
            if key:
 | 
			
		||||
                self._instance._mark_as_changed('%s.%s' % (self._name, key))
 | 
			
		||||
                self._instance._mark_as_changed("%s.%s" % (self._name, key))
 | 
			
		||||
            else:
 | 
			
		||||
                self._instance._mark_as_changed(self._name)
 | 
			
		||||
 | 
			
		||||
@@ -102,7 +112,7 @@ class BaseList(list):
 | 
			
		||||
    _name = None
 | 
			
		||||
 | 
			
		||||
    def __init__(self, list_items, instance, name):
 | 
			
		||||
        BaseDocument = _import_class('BaseDocument')
 | 
			
		||||
        BaseDocument = _import_class("BaseDocument")
 | 
			
		||||
 | 
			
		||||
        if isinstance(instance, BaseDocument):
 | 
			
		||||
            self._instance = weakref.proxy(instance)
 | 
			
		||||
@@ -117,17 +127,17 @@ class BaseList(list):
 | 
			
		||||
            # to parent's instance. This is buggy for now but would require more work to be handled properly
 | 
			
		||||
            return value
 | 
			
		||||
 | 
			
		||||
        EmbeddedDocument = _import_class('EmbeddedDocument')
 | 
			
		||||
        EmbeddedDocument = _import_class("EmbeddedDocument")
 | 
			
		||||
        if isinstance(value, EmbeddedDocument) and value._instance is None:
 | 
			
		||||
            value._instance = self._instance
 | 
			
		||||
        elif isinstance(value, dict) and not isinstance(value, BaseDict):
 | 
			
		||||
            # Replace dict by BaseDict
 | 
			
		||||
            value = BaseDict(value, None, '%s.%s' % (self._name, key))
 | 
			
		||||
            value = BaseDict(value, None, "%s.%s" % (self._name, key))
 | 
			
		||||
            super(BaseList, self).__setitem__(key, value)
 | 
			
		||||
            value._instance = self._instance
 | 
			
		||||
        elif isinstance(value, list) and not isinstance(value, BaseList):
 | 
			
		||||
            # Replace list by BaseList
 | 
			
		||||
            value = BaseList(value, None, '%s.%s' % (self._name, key))
 | 
			
		||||
            value = BaseList(value, None, "%s.%s" % (self._name, key))
 | 
			
		||||
            super(BaseList, self).__setitem__(key, value)
 | 
			
		||||
            value._instance = self._instance
 | 
			
		||||
        return value
 | 
			
		||||
@@ -181,17 +191,14 @@ class BaseList(list):
 | 
			
		||||
            return self.__getitem__(slice(i, j))
 | 
			
		||||
 | 
			
		||||
    def _mark_as_changed(self, key=None):
 | 
			
		||||
        if hasattr(self._instance, '_mark_as_changed'):
 | 
			
		||||
        if hasattr(self._instance, "_mark_as_changed"):
 | 
			
		||||
            if key:
 | 
			
		||||
                self._instance._mark_as_changed(
 | 
			
		||||
                    '%s.%s' % (self._name, key % len(self))
 | 
			
		||||
                )
 | 
			
		||||
                self._instance._mark_as_changed("%s.%s" % (self._name, key % len(self)))
 | 
			
		||||
            else:
 | 
			
		||||
                self._instance._mark_as_changed(self._name)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EmbeddedDocumentList(BaseList):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, list_items, instance, name):
 | 
			
		||||
        super(EmbeddedDocumentList, self).__init__(list_items, instance, name)
 | 
			
		||||
        self._instance = instance
 | 
			
		||||
@@ -276,12 +283,10 @@ class EmbeddedDocumentList(BaseList):
 | 
			
		||||
        """
 | 
			
		||||
        values = self.__only_matches(self, kwargs)
 | 
			
		||||
        if len(values) == 0:
 | 
			
		||||
            raise DoesNotExist(
 | 
			
		||||
                '%s matching query does not exist.' % self._name
 | 
			
		||||
            )
 | 
			
		||||
            raise DoesNotExist("%s matching query does not exist." % self._name)
 | 
			
		||||
        elif len(values) > 1:
 | 
			
		||||
            raise MultipleObjectsReturned(
 | 
			
		||||
                '%d items returned, instead of 1' % len(values)
 | 
			
		||||
                "%d items returned, instead of 1" % len(values)
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        return values[0]
 | 
			
		||||
@@ -362,7 +367,7 @@ class EmbeddedDocumentList(BaseList):
 | 
			
		||||
 | 
			
		||||
class StrictDict(object):
 | 
			
		||||
    __slots__ = ()
 | 
			
		||||
    _special_fields = {'get', 'pop', 'iteritems', 'items', 'keys', 'create'}
 | 
			
		||||
    _special_fields = {"get", "pop", "iteritems", "items", "keys", "create"}
 | 
			
		||||
    _classes = {}
 | 
			
		||||
 | 
			
		||||
    def __init__(self, **kwargs):
 | 
			
		||||
@@ -370,14 +375,14 @@ class StrictDict(object):
 | 
			
		||||
            setattr(self, k, v)
 | 
			
		||||
 | 
			
		||||
    def __getitem__(self, key):
 | 
			
		||||
        key = '_reserved_' + key if key in self._special_fields else key
 | 
			
		||||
        key = "_reserved_" + key if key in self._special_fields else key
 | 
			
		||||
        try:
 | 
			
		||||
            return getattr(self, key)
 | 
			
		||||
        except AttributeError:
 | 
			
		||||
            raise KeyError(key)
 | 
			
		||||
 | 
			
		||||
    def __setitem__(self, key, value):
 | 
			
		||||
        key = '_reserved_' + key if key in self._special_fields else key
 | 
			
		||||
        key = "_reserved_" + key if key in self._special_fields else key
 | 
			
		||||
        return setattr(self, key, value)
 | 
			
		||||
 | 
			
		||||
    def __contains__(self, key):
 | 
			
		||||
@@ -424,27 +429,32 @@ class StrictDict(object):
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def create(cls, allowed_keys):
 | 
			
		||||
        allowed_keys_tuple = tuple(('_reserved_' + k if k in cls._special_fields else k) for k in allowed_keys)
 | 
			
		||||
        allowed_keys_tuple = tuple(
 | 
			
		||||
            ("_reserved_" + k if k in cls._special_fields else k) for k in allowed_keys
 | 
			
		||||
        )
 | 
			
		||||
        allowed_keys = frozenset(allowed_keys_tuple)
 | 
			
		||||
        if allowed_keys not in cls._classes:
 | 
			
		||||
 | 
			
		||||
            class SpecificStrictDict(cls):
 | 
			
		||||
                __slots__ = allowed_keys_tuple
 | 
			
		||||
 | 
			
		||||
                def __repr__(self):
 | 
			
		||||
                    return '{%s}' % ', '.join('"{0!s}": {1!r}'.format(k, v) for k, v in self.items())
 | 
			
		||||
                    return "{%s}" % ", ".join(
 | 
			
		||||
                        '"{0!s}": {1!r}'.format(k, v) for k, v in self.items()
 | 
			
		||||
                    )
 | 
			
		||||
 | 
			
		||||
            cls._classes[allowed_keys] = SpecificStrictDict
 | 
			
		||||
        return cls._classes[allowed_keys]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LazyReference(DBRef):
 | 
			
		||||
    __slots__ = ('_cached_doc', 'passthrough', 'document_type')
 | 
			
		||||
    __slots__ = ("_cached_doc", "passthrough", "document_type")
 | 
			
		||||
 | 
			
		||||
    def fetch(self, force=False):
 | 
			
		||||
        if not self._cached_doc or force:
 | 
			
		||||
            self._cached_doc = self.document_type.objects.get(pk=self.pk)
 | 
			
		||||
            if not self._cached_doc:
 | 
			
		||||
                raise DoesNotExist('Trying to dereference unknown document %s' % (self))
 | 
			
		||||
                raise DoesNotExist("Trying to dereference unknown document %s" % (self))
 | 
			
		||||
        return self._cached_doc
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
@@ -455,7 +465,9 @@ class LazyReference(DBRef):
 | 
			
		||||
        self.document_type = document_type
 | 
			
		||||
        self._cached_doc = cached_doc
 | 
			
		||||
        self.passthrough = passthrough
 | 
			
		||||
        super(LazyReference, self).__init__(self.document_type._get_collection_name(), pk)
 | 
			
		||||
        super(LazyReference, self).__init__(
 | 
			
		||||
            self.document_type._get_collection_name(), pk
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def __getitem__(self, name):
 | 
			
		||||
        if not self.passthrough:
 | 
			
		||||
@@ -464,7 +476,7 @@ class LazyReference(DBRef):
 | 
			
		||||
        return document[name]
 | 
			
		||||
 | 
			
		||||
    def __getattr__(self, name):
 | 
			
		||||
        if not object.__getattribute__(self, 'passthrough'):
 | 
			
		||||
        if not object.__getattribute__(self, "passthrough"):
 | 
			
		||||
            raise AttributeError()
 | 
			
		||||
        document = self.fetch()
 | 
			
		||||
        try:
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -8,13 +8,11 @@ import six
 | 
			
		||||
from six import iteritems
 | 
			
		||||
 | 
			
		||||
from mongoengine.base.common import UPDATE_OPERATORS
 | 
			
		||||
from mongoengine.base.datastructures import (BaseDict, BaseList,
 | 
			
		||||
                                             EmbeddedDocumentList)
 | 
			
		||||
from mongoengine.base.datastructures import BaseDict, BaseList, EmbeddedDocumentList
 | 
			
		||||
from mongoengine.common import _import_class
 | 
			
		||||
from mongoengine.errors import DeprecatedError, ValidationError
 | 
			
		||||
 | 
			
		||||
__all__ = ('BaseField', 'ComplexBaseField', 'ObjectIdField',
 | 
			
		||||
           'GeoJsonBaseField')
 | 
			
		||||
__all__ = ("BaseField", "ComplexBaseField", "ObjectIdField", "GeoJsonBaseField")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BaseField(object):
 | 
			
		||||
@@ -23,6 +21,7 @@ class BaseField(object):
 | 
			
		||||
 | 
			
		||||
    .. versionchanged:: 0.5 - added verbose and help text
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    name = None
 | 
			
		||||
    _geo_index = False
 | 
			
		||||
    _auto_gen = False  # Call `generate` to generate a value
 | 
			
		||||
@@ -34,10 +33,21 @@ class BaseField(object):
 | 
			
		||||
    creation_counter = 0
 | 
			
		||||
    auto_creation_counter = -1
 | 
			
		||||
 | 
			
		||||
    def __init__(self, db_field=None, name=None, required=False, default=None,
 | 
			
		||||
                 unique=False, unique_with=None, primary_key=False,
 | 
			
		||||
                 validation=None, choices=None, null=False, sparse=False,
 | 
			
		||||
                 **kwargs):
 | 
			
		||||
    def __init__(
 | 
			
		||||
        self,
 | 
			
		||||
        db_field=None,
 | 
			
		||||
        name=None,
 | 
			
		||||
        required=False,
 | 
			
		||||
        default=None,
 | 
			
		||||
        unique=False,
 | 
			
		||||
        unique_with=None,
 | 
			
		||||
        primary_key=False,
 | 
			
		||||
        validation=None,
 | 
			
		||||
        choices=None,
 | 
			
		||||
        null=False,
 | 
			
		||||
        sparse=False,
 | 
			
		||||
        **kwargs
 | 
			
		||||
    ):
 | 
			
		||||
        """
 | 
			
		||||
        :param db_field: The database field to store this field in
 | 
			
		||||
            (defaults to the name of the field)
 | 
			
		||||
@@ -65,7 +75,7 @@ class BaseField(object):
 | 
			
		||||
            existing attributes. Common metadata includes `verbose_name` and
 | 
			
		||||
            `help_text`.
 | 
			
		||||
        """
 | 
			
		||||
        self.db_field = (db_field or name) if not primary_key else '_id'
 | 
			
		||||
        self.db_field = (db_field or name) if not primary_key else "_id"
 | 
			
		||||
 | 
			
		||||
        if name:
 | 
			
		||||
            msg = 'Field\'s "name" attribute deprecated in favour of "db_field"'
 | 
			
		||||
@@ -82,17 +92,16 @@ class BaseField(object):
 | 
			
		||||
        self._owner_document = None
 | 
			
		||||
 | 
			
		||||
        # Make sure db_field is a string (if it's explicitly defined).
 | 
			
		||||
        if (
 | 
			
		||||
            self.db_field is not None and
 | 
			
		||||
            not isinstance(self.db_field, six.string_types)
 | 
			
		||||
        if self.db_field is not None and not isinstance(
 | 
			
		||||
            self.db_field, six.string_types
 | 
			
		||||
        ):
 | 
			
		||||
            raise TypeError('db_field should be a string.')
 | 
			
		||||
            raise TypeError("db_field should be a string.")
 | 
			
		||||
 | 
			
		||||
        # Make sure db_field doesn't contain any forbidden characters.
 | 
			
		||||
        if isinstance(self.db_field, six.string_types) and (
 | 
			
		||||
            '.' in self.db_field or
 | 
			
		||||
            '\0' in self.db_field or
 | 
			
		||||
            self.db_field.startswith('$')
 | 
			
		||||
            "." in self.db_field
 | 
			
		||||
            or "\0" in self.db_field
 | 
			
		||||
            or self.db_field.startswith("$")
 | 
			
		||||
        ):
 | 
			
		||||
            raise ValueError(
 | 
			
		||||
                'field names cannot contain dots (".") or null characters '
 | 
			
		||||
@@ -102,15 +111,17 @@ class BaseField(object):
 | 
			
		||||
        # Detect and report conflicts between metadata and base properties.
 | 
			
		||||
        conflicts = set(dir(self)) & set(kwargs)
 | 
			
		||||
        if conflicts:
 | 
			
		||||
            raise TypeError('%s already has attribute(s): %s' % (
 | 
			
		||||
                self.__class__.__name__, ', '.join(conflicts)))
 | 
			
		||||
            raise TypeError(
 | 
			
		||||
                "%s already has attribute(s): %s"
 | 
			
		||||
                % (self.__class__.__name__, ", ".join(conflicts))
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        # Assign metadata to the instance
 | 
			
		||||
        # This efficient method is available because no __slots__ are defined.
 | 
			
		||||
        self.__dict__.update(kwargs)
 | 
			
		||||
 | 
			
		||||
        # Adjust the appropriate creation counter, and save our local copy.
 | 
			
		||||
        if self.db_field == '_id':
 | 
			
		||||
        if self.db_field == "_id":
 | 
			
		||||
            self.creation_counter = BaseField.auto_creation_counter
 | 
			
		||||
            BaseField.auto_creation_counter -= 1
 | 
			
		||||
        else:
 | 
			
		||||
@@ -142,8 +153,8 @@ class BaseField(object):
 | 
			
		||||
        if instance._initialised:
 | 
			
		||||
            try:
 | 
			
		||||
                value_has_changed = (
 | 
			
		||||
                    self.name not in instance._data or
 | 
			
		||||
                    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)
 | 
			
		||||
@@ -153,7 +164,7 @@ class BaseField(object):
 | 
			
		||||
                # Mark the field as changed in such cases.
 | 
			
		||||
                instance._mark_as_changed(self.name)
 | 
			
		||||
 | 
			
		||||
        EmbeddedDocument = _import_class('EmbeddedDocument')
 | 
			
		||||
        EmbeddedDocument = _import_class("EmbeddedDocument")
 | 
			
		||||
        if isinstance(value, EmbeddedDocument):
 | 
			
		||||
            value._instance = weakref.proxy(instance)
 | 
			
		||||
        elif isinstance(value, (list, tuple)):
 | 
			
		||||
@@ -163,7 +174,7 @@ class BaseField(object):
 | 
			
		||||
 | 
			
		||||
        instance._data[self.name] = value
 | 
			
		||||
 | 
			
		||||
    def error(self, message='', errors=None, field_name=None):
 | 
			
		||||
    def error(self, message="", errors=None, field_name=None):
 | 
			
		||||
        """Raise a ValidationError."""
 | 
			
		||||
        field_name = field_name if field_name else self.name
 | 
			
		||||
        raise ValidationError(message, errors=errors, field_name=field_name)
 | 
			
		||||
@@ -180,11 +191,11 @@ class BaseField(object):
 | 
			
		||||
        """Helper method to call to_mongo with proper inputs."""
 | 
			
		||||
        f_inputs = self.to_mongo.__code__.co_varnames
 | 
			
		||||
        ex_vars = {}
 | 
			
		||||
        if 'fields' in f_inputs:
 | 
			
		||||
            ex_vars['fields'] = fields
 | 
			
		||||
        if "fields" in f_inputs:
 | 
			
		||||
            ex_vars["fields"] = fields
 | 
			
		||||
 | 
			
		||||
        if 'use_db_field' in f_inputs:
 | 
			
		||||
            ex_vars['use_db_field'] = use_db_field
 | 
			
		||||
        if "use_db_field" in f_inputs:
 | 
			
		||||
            ex_vars["use_db_field"] = use_db_field
 | 
			
		||||
 | 
			
		||||
        return self.to_mongo(value, **ex_vars)
 | 
			
		||||
 | 
			
		||||
@@ -199,8 +210,8 @@ class BaseField(object):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def _validate_choices(self, value):
 | 
			
		||||
        Document = _import_class('Document')
 | 
			
		||||
        EmbeddedDocument = _import_class('EmbeddedDocument')
 | 
			
		||||
        Document = _import_class("Document")
 | 
			
		||||
        EmbeddedDocument = _import_class("EmbeddedDocument")
 | 
			
		||||
 | 
			
		||||
        choice_list = self.choices
 | 
			
		||||
        if isinstance(next(iter(choice_list)), (list, tuple)):
 | 
			
		||||
@@ -211,15 +222,13 @@ class BaseField(object):
 | 
			
		||||
        if isinstance(value, (Document, EmbeddedDocument)):
 | 
			
		||||
            if not any(isinstance(value, c) for c in choice_list):
 | 
			
		||||
                self.error(
 | 
			
		||||
                    'Value must be an instance of %s' % (
 | 
			
		||||
                        six.text_type(choice_list)
 | 
			
		||||
                    )
 | 
			
		||||
                    "Value must be an instance of %s" % (six.text_type(choice_list))
 | 
			
		||||
                )
 | 
			
		||||
        # Choices which are types other than Documents
 | 
			
		||||
        else:
 | 
			
		||||
            values = value if isinstance(value, (list, tuple)) else [value]
 | 
			
		||||
            if len(set(values) - set(choice_list)):
 | 
			
		||||
                self.error('Value must be one of %s' % six.text_type(choice_list))
 | 
			
		||||
                self.error("Value must be one of %s" % six.text_type(choice_list))
 | 
			
		||||
 | 
			
		||||
    def _validate(self, value, **kwargs):
 | 
			
		||||
        # Check the Choices Constraint
 | 
			
		||||
@@ -235,13 +244,17 @@ class BaseField(object):
 | 
			
		||||
                    # in favor of having validation raising a ValidationError
 | 
			
		||||
                    ret = self.validation(value)
 | 
			
		||||
                    if ret is not None:
 | 
			
		||||
                        raise DeprecatedError('validation argument for `%s` must not return anything, '
 | 
			
		||||
                                              'it should raise a ValidationError if validation fails' % self.name)
 | 
			
		||||
                        raise DeprecatedError(
 | 
			
		||||
                            "validation argument for `%s` must not return anything, "
 | 
			
		||||
                            "it should raise a ValidationError if validation fails"
 | 
			
		||||
                            % self.name
 | 
			
		||||
                        )
 | 
			
		||||
                except ValidationError as ex:
 | 
			
		||||
                    self.error(str(ex))
 | 
			
		||||
            else:
 | 
			
		||||
                raise ValueError('validation argument for `"%s"` must be a '
 | 
			
		||||
                                 'callable.' % self.name)
 | 
			
		||||
                raise ValueError(
 | 
			
		||||
                    'validation argument for `"%s"` must be a ' "callable." % self.name
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
        self.validate(value, **kwargs)
 | 
			
		||||
 | 
			
		||||
@@ -275,35 +288,41 @@ class ComplexBaseField(BaseField):
 | 
			
		||||
            # Document class being used rather than a document object
 | 
			
		||||
            return self
 | 
			
		||||
 | 
			
		||||
        ReferenceField = _import_class('ReferenceField')
 | 
			
		||||
        GenericReferenceField = _import_class('GenericReferenceField')
 | 
			
		||||
        EmbeddedDocumentListField = _import_class('EmbeddedDocumentListField')
 | 
			
		||||
        ReferenceField = _import_class("ReferenceField")
 | 
			
		||||
        GenericReferenceField = _import_class("GenericReferenceField")
 | 
			
		||||
        EmbeddedDocumentListField = _import_class("EmbeddedDocumentListField")
 | 
			
		||||
 | 
			
		||||
        auto_dereference = instance._fields[self.name]._auto_dereference
 | 
			
		||||
 | 
			
		||||
        dereference = (auto_dereference and
 | 
			
		||||
                       (self.field is None or isinstance(self.field,
 | 
			
		||||
                                                         (GenericReferenceField, ReferenceField))))
 | 
			
		||||
        dereference = auto_dereference and (
 | 
			
		||||
            self.field is None
 | 
			
		||||
            or isinstance(self.field, (GenericReferenceField, ReferenceField))
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        _dereference = _import_class('DeReference')()
 | 
			
		||||
        _dereference = _import_class("DeReference")()
 | 
			
		||||
 | 
			
		||||
        if (instance._initialised and
 | 
			
		||||
                dereference and
 | 
			
		||||
                instance._data.get(self.name) and
 | 
			
		||||
                not getattr(instance._data[self.name], '_dereferenced', False)):
 | 
			
		||||
        if (
 | 
			
		||||
            instance._initialised
 | 
			
		||||
            and dereference
 | 
			
		||||
            and instance._data.get(self.name)
 | 
			
		||||
            and not getattr(instance._data[self.name], "_dereferenced", False)
 | 
			
		||||
        ):
 | 
			
		||||
            instance._data[self.name] = _dereference(
 | 
			
		||||
                instance._data.get(self.name), max_depth=1, instance=instance,
 | 
			
		||||
                name=self.name
 | 
			
		||||
                instance._data.get(self.name),
 | 
			
		||||
                max_depth=1,
 | 
			
		||||
                instance=instance,
 | 
			
		||||
                name=self.name,
 | 
			
		||||
            )
 | 
			
		||||
            if hasattr(instance._data[self.name], '_dereferenced'):
 | 
			
		||||
            if hasattr(instance._data[self.name], "_dereferenced"):
 | 
			
		||||
                instance._data[self.name]._dereferenced = True
 | 
			
		||||
 | 
			
		||||
        value = super(ComplexBaseField, self).__get__(instance, owner)
 | 
			
		||||
 | 
			
		||||
        # Convert lists / values so we can watch for any changes on them
 | 
			
		||||
        if isinstance(value, (list, tuple)):
 | 
			
		||||
            if (issubclass(type(self), EmbeddedDocumentListField) and
 | 
			
		||||
                    not isinstance(value, EmbeddedDocumentList)):
 | 
			
		||||
            if issubclass(type(self), EmbeddedDocumentListField) and not isinstance(
 | 
			
		||||
                value, EmbeddedDocumentList
 | 
			
		||||
            ):
 | 
			
		||||
                value = EmbeddedDocumentList(value, instance, self.name)
 | 
			
		||||
            elif not isinstance(value, BaseList):
 | 
			
		||||
                value = BaseList(value, instance, self.name)
 | 
			
		||||
@@ -312,12 +331,13 @@ class ComplexBaseField(BaseField):
 | 
			
		||||
            value = BaseDict(value, instance, self.name)
 | 
			
		||||
            instance._data[self.name] = value
 | 
			
		||||
 | 
			
		||||
        if (auto_dereference and instance._initialised and
 | 
			
		||||
                isinstance(value, (BaseList, BaseDict)) and
 | 
			
		||||
                not value._dereferenced):
 | 
			
		||||
            value = _dereference(
 | 
			
		||||
                value, max_depth=1, instance=instance, name=self.name
 | 
			
		||||
            )
 | 
			
		||||
        if (
 | 
			
		||||
            auto_dereference
 | 
			
		||||
            and instance._initialised
 | 
			
		||||
            and isinstance(value, (BaseList, BaseDict))
 | 
			
		||||
            and not value._dereferenced
 | 
			
		||||
        ):
 | 
			
		||||
            value = _dereference(value, max_depth=1, instance=instance, name=self.name)
 | 
			
		||||
            value._dereferenced = True
 | 
			
		||||
            instance._data[self.name] = value
 | 
			
		||||
 | 
			
		||||
@@ -328,16 +348,16 @@ class ComplexBaseField(BaseField):
 | 
			
		||||
        if isinstance(value, six.string_types):
 | 
			
		||||
            return value
 | 
			
		||||
 | 
			
		||||
        if hasattr(value, 'to_python'):
 | 
			
		||||
        if hasattr(value, "to_python"):
 | 
			
		||||
            return value.to_python()
 | 
			
		||||
 | 
			
		||||
        BaseDocument = _import_class('BaseDocument')
 | 
			
		||||
        BaseDocument = _import_class("BaseDocument")
 | 
			
		||||
        if isinstance(value, BaseDocument):
 | 
			
		||||
            # Something is wrong, return the value as it is
 | 
			
		||||
            return value
 | 
			
		||||
 | 
			
		||||
        is_list = False
 | 
			
		||||
        if not hasattr(value, 'items'):
 | 
			
		||||
        if not hasattr(value, "items"):
 | 
			
		||||
            try:
 | 
			
		||||
                is_list = True
 | 
			
		||||
                value = {idx: v for idx, v in enumerate(value)}
 | 
			
		||||
@@ -346,50 +366,54 @@ class ComplexBaseField(BaseField):
 | 
			
		||||
 | 
			
		||||
        if self.field:
 | 
			
		||||
            self.field._auto_dereference = self._auto_dereference
 | 
			
		||||
            value_dict = {key: self.field.to_python(item)
 | 
			
		||||
                          for key, item in value.items()}
 | 
			
		||||
            value_dict = {
 | 
			
		||||
                key: self.field.to_python(item) for key, item in value.items()
 | 
			
		||||
            }
 | 
			
		||||
        else:
 | 
			
		||||
            Document = _import_class('Document')
 | 
			
		||||
            Document = _import_class("Document")
 | 
			
		||||
            value_dict = {}
 | 
			
		||||
            for k, v in value.items():
 | 
			
		||||
                if isinstance(v, Document):
 | 
			
		||||
                    # We need the id from the saved object to create the DBRef
 | 
			
		||||
                    if v.pk is None:
 | 
			
		||||
                        self.error('You can only reference documents once they'
 | 
			
		||||
                                   ' have been saved to the database')
 | 
			
		||||
                        self.error(
 | 
			
		||||
                            "You can only reference documents once they"
 | 
			
		||||
                            " have been saved to the database"
 | 
			
		||||
                        )
 | 
			
		||||
                    collection = v._get_collection_name()
 | 
			
		||||
                    value_dict[k] = DBRef(collection, v.pk)
 | 
			
		||||
                elif hasattr(v, 'to_python'):
 | 
			
		||||
                elif hasattr(v, "to_python"):
 | 
			
		||||
                    value_dict[k] = v.to_python()
 | 
			
		||||
                else:
 | 
			
		||||
                    value_dict[k] = self.to_python(v)
 | 
			
		||||
 | 
			
		||||
        if is_list:  # Convert back to a list
 | 
			
		||||
            return [v for _, v in sorted(value_dict.items(),
 | 
			
		||||
                                         key=operator.itemgetter(0))]
 | 
			
		||||
            return [
 | 
			
		||||
                v for _, v in sorted(value_dict.items(), key=operator.itemgetter(0))
 | 
			
		||||
            ]
 | 
			
		||||
        return value_dict
 | 
			
		||||
 | 
			
		||||
    def to_mongo(self, value, use_db_field=True, fields=None):
 | 
			
		||||
        """Convert a Python type to a MongoDB-compatible type."""
 | 
			
		||||
        Document = _import_class('Document')
 | 
			
		||||
        EmbeddedDocument = _import_class('EmbeddedDocument')
 | 
			
		||||
        GenericReferenceField = _import_class('GenericReferenceField')
 | 
			
		||||
        Document = _import_class("Document")
 | 
			
		||||
        EmbeddedDocument = _import_class("EmbeddedDocument")
 | 
			
		||||
        GenericReferenceField = _import_class("GenericReferenceField")
 | 
			
		||||
 | 
			
		||||
        if isinstance(value, six.string_types):
 | 
			
		||||
            return value
 | 
			
		||||
 | 
			
		||||
        if hasattr(value, 'to_mongo'):
 | 
			
		||||
        if hasattr(value, "to_mongo"):
 | 
			
		||||
            if isinstance(value, Document):
 | 
			
		||||
                return GenericReferenceField().to_mongo(value)
 | 
			
		||||
            cls = value.__class__
 | 
			
		||||
            val = value.to_mongo(use_db_field, fields)
 | 
			
		||||
            # If it's a document that is not inherited add _cls
 | 
			
		||||
            if isinstance(value, EmbeddedDocument):
 | 
			
		||||
                val['_cls'] = cls.__name__
 | 
			
		||||
                val["_cls"] = cls.__name__
 | 
			
		||||
            return val
 | 
			
		||||
 | 
			
		||||
        is_list = False
 | 
			
		||||
        if not hasattr(value, 'items'):
 | 
			
		||||
        if not hasattr(value, "items"):
 | 
			
		||||
            try:
 | 
			
		||||
                is_list = True
 | 
			
		||||
                value = {k: v for k, v in enumerate(value)}
 | 
			
		||||
@@ -407,39 +431,42 @@ class ComplexBaseField(BaseField):
 | 
			
		||||
                if isinstance(v, Document):
 | 
			
		||||
                    # We need the id from the saved object to create the DBRef
 | 
			
		||||
                    if v.pk is None:
 | 
			
		||||
                        self.error('You can only reference documents once they'
 | 
			
		||||
                                   ' have been saved to the database')
 | 
			
		||||
                        self.error(
 | 
			
		||||
                            "You can only reference documents once they"
 | 
			
		||||
                            " have been saved to the database"
 | 
			
		||||
                        )
 | 
			
		||||
 | 
			
		||||
                    # If its a document that is not inheritable it won't have
 | 
			
		||||
                    # any _cls data so make it a generic reference allows
 | 
			
		||||
                    # us to dereference
 | 
			
		||||
                    meta = getattr(v, '_meta', {})
 | 
			
		||||
                    allow_inheritance = meta.get('allow_inheritance')
 | 
			
		||||
                    meta = getattr(v, "_meta", {})
 | 
			
		||||
                    allow_inheritance = meta.get("allow_inheritance")
 | 
			
		||||
                    if not allow_inheritance and not self.field:
 | 
			
		||||
                        value_dict[k] = GenericReferenceField().to_mongo(v)
 | 
			
		||||
                    else:
 | 
			
		||||
                        collection = v._get_collection_name()
 | 
			
		||||
                        value_dict[k] = DBRef(collection, v.pk)
 | 
			
		||||
                elif hasattr(v, 'to_mongo'):
 | 
			
		||||
                elif hasattr(v, "to_mongo"):
 | 
			
		||||
                    cls = v.__class__
 | 
			
		||||
                    val = v.to_mongo(use_db_field, fields)
 | 
			
		||||
                    # If it's a document that is not inherited add _cls
 | 
			
		||||
                    if isinstance(v, (Document, EmbeddedDocument)):
 | 
			
		||||
                        val['_cls'] = cls.__name__
 | 
			
		||||
                        val["_cls"] = cls.__name__
 | 
			
		||||
                    value_dict[k] = val
 | 
			
		||||
                else:
 | 
			
		||||
                    value_dict[k] = self.to_mongo(v, use_db_field, fields)
 | 
			
		||||
 | 
			
		||||
        if is_list:  # Convert back to a list
 | 
			
		||||
            return [v for _, v in sorted(value_dict.items(),
 | 
			
		||||
                                         key=operator.itemgetter(0))]
 | 
			
		||||
            return [
 | 
			
		||||
                v for _, v in sorted(value_dict.items(), key=operator.itemgetter(0))
 | 
			
		||||
            ]
 | 
			
		||||
        return value_dict
 | 
			
		||||
 | 
			
		||||
    def validate(self, value):
 | 
			
		||||
        """If field is provided ensure the value is valid."""
 | 
			
		||||
        errors = {}
 | 
			
		||||
        if self.field:
 | 
			
		||||
            if hasattr(value, 'iteritems') or hasattr(value, 'items'):
 | 
			
		||||
            if hasattr(value, "iteritems") or hasattr(value, "items"):
 | 
			
		||||
                sequence = iteritems(value)
 | 
			
		||||
            else:
 | 
			
		||||
                sequence = enumerate(value)
 | 
			
		||||
@@ -453,11 +480,10 @@ class ComplexBaseField(BaseField):
 | 
			
		||||
 | 
			
		||||
            if errors:
 | 
			
		||||
                field_class = self.field.__class__.__name__
 | 
			
		||||
                self.error('Invalid %s item (%s)' % (field_class, value),
 | 
			
		||||
                           errors=errors)
 | 
			
		||||
                self.error("Invalid %s item (%s)" % (field_class, value), errors=errors)
 | 
			
		||||
        # Don't allow empty values if required
 | 
			
		||||
        if self.required and not value:
 | 
			
		||||
            self.error('Field is required and cannot be empty')
 | 
			
		||||
            self.error("Field is required and cannot be empty")
 | 
			
		||||
 | 
			
		||||
    def prepare_query_value(self, op, value):
 | 
			
		||||
        return self.to_mongo(value)
 | 
			
		||||
@@ -500,7 +526,7 @@ class ObjectIdField(BaseField):
 | 
			
		||||
        try:
 | 
			
		||||
            ObjectId(six.text_type(value))
 | 
			
		||||
        except Exception:
 | 
			
		||||
            self.error('Invalid Object ID')
 | 
			
		||||
            self.error("Invalid Object ID")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GeoJsonBaseField(BaseField):
 | 
			
		||||
@@ -510,14 +536,14 @@ class GeoJsonBaseField(BaseField):
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    _geo_index = pymongo.GEOSPHERE
 | 
			
		||||
    _type = 'GeoBase'
 | 
			
		||||
    _type = "GeoBase"
 | 
			
		||||
 | 
			
		||||
    def __init__(self, auto_index=True, *args, **kwargs):
 | 
			
		||||
        """
 | 
			
		||||
        :param bool auto_index: Automatically create a '2dsphere' index.\
 | 
			
		||||
            Defaults to `True`.
 | 
			
		||||
        """
 | 
			
		||||
        self._name = '%sField' % self._type
 | 
			
		||||
        self._name = "%sField" % self._type
 | 
			
		||||
        if not auto_index:
 | 
			
		||||
            self._geo_index = False
 | 
			
		||||
        super(GeoJsonBaseField, self).__init__(*args, **kwargs)
 | 
			
		||||
@@ -525,57 +551,58 @@ class GeoJsonBaseField(BaseField):
 | 
			
		||||
    def validate(self, value):
 | 
			
		||||
        """Validate the GeoJson object based on its type."""
 | 
			
		||||
        if isinstance(value, dict):
 | 
			
		||||
            if set(value.keys()) == {'type', 'coordinates'}:
 | 
			
		||||
                if value['type'] != self._type:
 | 
			
		||||
                    self.error('%s type must be "%s"' %
 | 
			
		||||
                               (self._name, self._type))
 | 
			
		||||
                return self.validate(value['coordinates'])
 | 
			
		||||
            if set(value.keys()) == {"type", "coordinates"}:
 | 
			
		||||
                if value["type"] != self._type:
 | 
			
		||||
                    self.error('%s type must be "%s"' % (self._name, self._type))
 | 
			
		||||
                return self.validate(value["coordinates"])
 | 
			
		||||
            else:
 | 
			
		||||
                self.error('%s can only accept a valid GeoJson dictionary'
 | 
			
		||||
                           ' or lists of (x, y)' % self._name)
 | 
			
		||||
                self.error(
 | 
			
		||||
                    "%s can only accept a valid GeoJson dictionary"
 | 
			
		||||
                    " or lists of (x, y)" % self._name
 | 
			
		||||
                )
 | 
			
		||||
                return
 | 
			
		||||
        elif not isinstance(value, (list, tuple)):
 | 
			
		||||
            self.error('%s can only accept lists of [x, y]' % self._name)
 | 
			
		||||
            self.error("%s can only accept lists of [x, y]" % self._name)
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        validate = getattr(self, '_validate_%s' % self._type.lower())
 | 
			
		||||
        validate = getattr(self, "_validate_%s" % self._type.lower())
 | 
			
		||||
        error = validate(value)
 | 
			
		||||
        if error:
 | 
			
		||||
            self.error(error)
 | 
			
		||||
 | 
			
		||||
    def _validate_polygon(self, value, top_level=True):
 | 
			
		||||
        if not isinstance(value, (list, tuple)):
 | 
			
		||||
            return 'Polygons must contain list of linestrings'
 | 
			
		||||
            return "Polygons must contain list of linestrings"
 | 
			
		||||
 | 
			
		||||
        # Quick and dirty validator
 | 
			
		||||
        try:
 | 
			
		||||
            value[0][0][0]
 | 
			
		||||
        except (TypeError, IndexError):
 | 
			
		||||
            return 'Invalid Polygon must contain at least one valid linestring'
 | 
			
		||||
            return "Invalid Polygon must contain at least one valid linestring"
 | 
			
		||||
 | 
			
		||||
        errors = []
 | 
			
		||||
        for val in value:
 | 
			
		||||
            error = self._validate_linestring(val, False)
 | 
			
		||||
            if not error and val[0] != val[-1]:
 | 
			
		||||
                error = 'LineStrings must start and end at the same point'
 | 
			
		||||
                error = "LineStrings must start and end at the same point"
 | 
			
		||||
            if error and error not in errors:
 | 
			
		||||
                errors.append(error)
 | 
			
		||||
        if errors:
 | 
			
		||||
            if top_level:
 | 
			
		||||
                return 'Invalid Polygon:\n%s' % ', '.join(errors)
 | 
			
		||||
                return "Invalid Polygon:\n%s" % ", ".join(errors)
 | 
			
		||||
            else:
 | 
			
		||||
                return '%s' % ', '.join(errors)
 | 
			
		||||
                return "%s" % ", ".join(errors)
 | 
			
		||||
 | 
			
		||||
    def _validate_linestring(self, value, top_level=True):
 | 
			
		||||
        """Validate a linestring."""
 | 
			
		||||
        if not isinstance(value, (list, tuple)):
 | 
			
		||||
            return 'LineStrings must contain list of coordinate pairs'
 | 
			
		||||
            return "LineStrings must contain list of coordinate pairs"
 | 
			
		||||
 | 
			
		||||
        # Quick and dirty validator
 | 
			
		||||
        try:
 | 
			
		||||
            value[0][0]
 | 
			
		||||
        except (TypeError, IndexError):
 | 
			
		||||
            return 'Invalid LineString must contain at least one valid point'
 | 
			
		||||
            return "Invalid LineString must contain at least one valid point"
 | 
			
		||||
 | 
			
		||||
        errors = []
 | 
			
		||||
        for val in value:
 | 
			
		||||
@@ -584,29 +611,30 @@ class GeoJsonBaseField(BaseField):
 | 
			
		||||
                errors.append(error)
 | 
			
		||||
        if errors:
 | 
			
		||||
            if top_level:
 | 
			
		||||
                return 'Invalid LineString:\n%s' % ', '.join(errors)
 | 
			
		||||
                return "Invalid LineString:\n%s" % ", ".join(errors)
 | 
			
		||||
            else:
 | 
			
		||||
                return '%s' % ', '.join(errors)
 | 
			
		||||
                return "%s" % ", ".join(errors)
 | 
			
		||||
 | 
			
		||||
    def _validate_point(self, value):
 | 
			
		||||
        """Validate each set of coords"""
 | 
			
		||||
        if not isinstance(value, (list, tuple)):
 | 
			
		||||
            return 'Points must be a list of coordinate pairs'
 | 
			
		||||
            return "Points must be a list of coordinate pairs"
 | 
			
		||||
        elif not len(value) == 2:
 | 
			
		||||
            return 'Value (%s) must be a two-dimensional point' % repr(value)
 | 
			
		||||
        elif (not isinstance(value[0], (float, int)) or
 | 
			
		||||
              not isinstance(value[1], (float, int))):
 | 
			
		||||
            return 'Both values (%s) in point must be float or int' % repr(value)
 | 
			
		||||
            return "Value (%s) must be a two-dimensional point" % repr(value)
 | 
			
		||||
        elif not isinstance(value[0], (float, int)) or not isinstance(
 | 
			
		||||
            value[1], (float, int)
 | 
			
		||||
        ):
 | 
			
		||||
            return "Both values (%s) in point must be float or int" % repr(value)
 | 
			
		||||
 | 
			
		||||
    def _validate_multipoint(self, value):
 | 
			
		||||
        if not isinstance(value, (list, tuple)):
 | 
			
		||||
            return 'MultiPoint must be a list of Point'
 | 
			
		||||
            return "MultiPoint must be a list of Point"
 | 
			
		||||
 | 
			
		||||
        # Quick and dirty validator
 | 
			
		||||
        try:
 | 
			
		||||
            value[0][0]
 | 
			
		||||
        except (TypeError, IndexError):
 | 
			
		||||
            return 'Invalid MultiPoint must contain at least one valid point'
 | 
			
		||||
            return "Invalid MultiPoint must contain at least one valid point"
 | 
			
		||||
 | 
			
		||||
        errors = []
 | 
			
		||||
        for point in value:
 | 
			
		||||
@@ -615,17 +643,17 @@ class GeoJsonBaseField(BaseField):
 | 
			
		||||
                errors.append(error)
 | 
			
		||||
 | 
			
		||||
        if errors:
 | 
			
		||||
            return '%s' % ', '.join(errors)
 | 
			
		||||
            return "%s" % ", ".join(errors)
 | 
			
		||||
 | 
			
		||||
    def _validate_multilinestring(self, value, top_level=True):
 | 
			
		||||
        if not isinstance(value, (list, tuple)):
 | 
			
		||||
            return 'MultiLineString must be a list of LineString'
 | 
			
		||||
            return "MultiLineString must be a list of LineString"
 | 
			
		||||
 | 
			
		||||
        # Quick and dirty validator
 | 
			
		||||
        try:
 | 
			
		||||
            value[0][0][0]
 | 
			
		||||
        except (TypeError, IndexError):
 | 
			
		||||
            return 'Invalid MultiLineString must contain at least one valid linestring'
 | 
			
		||||
            return "Invalid MultiLineString must contain at least one valid linestring"
 | 
			
		||||
 | 
			
		||||
        errors = []
 | 
			
		||||
        for linestring in value:
 | 
			
		||||
@@ -635,19 +663,19 @@ class GeoJsonBaseField(BaseField):
 | 
			
		||||
 | 
			
		||||
        if errors:
 | 
			
		||||
            if top_level:
 | 
			
		||||
                return 'Invalid MultiLineString:\n%s' % ', '.join(errors)
 | 
			
		||||
                return "Invalid MultiLineString:\n%s" % ", ".join(errors)
 | 
			
		||||
            else:
 | 
			
		||||
                return '%s' % ', '.join(errors)
 | 
			
		||||
                return "%s" % ", ".join(errors)
 | 
			
		||||
 | 
			
		||||
    def _validate_multipolygon(self, value):
 | 
			
		||||
        if not isinstance(value, (list, tuple)):
 | 
			
		||||
            return 'MultiPolygon must be a list of Polygon'
 | 
			
		||||
            return "MultiPolygon must be a list of Polygon"
 | 
			
		||||
 | 
			
		||||
        # Quick and dirty validator
 | 
			
		||||
        try:
 | 
			
		||||
            value[0][0][0][0]
 | 
			
		||||
        except (TypeError, IndexError):
 | 
			
		||||
            return 'Invalid MultiPolygon must contain at least one valid Polygon'
 | 
			
		||||
            return "Invalid MultiPolygon must contain at least one valid Polygon"
 | 
			
		||||
 | 
			
		||||
        errors = []
 | 
			
		||||
        for polygon in value:
 | 
			
		||||
@@ -656,9 +684,9 @@ class GeoJsonBaseField(BaseField):
 | 
			
		||||
                errors.append(error)
 | 
			
		||||
 | 
			
		||||
        if errors:
 | 
			
		||||
            return 'Invalid MultiPolygon:\n%s' % ', '.join(errors)
 | 
			
		||||
            return "Invalid MultiPolygon:\n%s" % ", ".join(errors)
 | 
			
		||||
 | 
			
		||||
    def to_mongo(self, value):
 | 
			
		||||
        if isinstance(value, dict):
 | 
			
		||||
            return value
 | 
			
		||||
        return SON([('type', self._type), ('coordinates', value)])
 | 
			
		||||
        return SON([("type", self._type), ("coordinates", value)])
 | 
			
		||||
 
 | 
			
		||||
@@ -8,12 +8,15 @@ from mongoengine.base.common import _document_registry
 | 
			
		||||
from mongoengine.base.fields import BaseField, ComplexBaseField, ObjectIdField
 | 
			
		||||
from mongoengine.common import _import_class
 | 
			
		||||
from mongoengine.errors import InvalidDocumentError
 | 
			
		||||
from mongoengine.queryset import (DO_NOTHING, DoesNotExist,
 | 
			
		||||
                                  MultipleObjectsReturned,
 | 
			
		||||
                                  QuerySetManager)
 | 
			
		||||
from mongoengine.queryset import (
 | 
			
		||||
    DO_NOTHING,
 | 
			
		||||
    DoesNotExist,
 | 
			
		||||
    MultipleObjectsReturned,
 | 
			
		||||
    QuerySetManager,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ('DocumentMetaclass', 'TopLevelDocumentMetaclass')
 | 
			
		||||
__all__ = ("DocumentMetaclass", "TopLevelDocumentMetaclass")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DocumentMetaclass(type):
 | 
			
		||||
@@ -25,44 +28,46 @@ class DocumentMetaclass(type):
 | 
			
		||||
        super_new = super(DocumentMetaclass, mcs).__new__
 | 
			
		||||
 | 
			
		||||
        # If a base class just call super
 | 
			
		||||
        metaclass = attrs.get('my_metaclass')
 | 
			
		||||
        metaclass = attrs.get("my_metaclass")
 | 
			
		||||
        if metaclass and issubclass(metaclass, DocumentMetaclass):
 | 
			
		||||
            return super_new(mcs, name, bases, attrs)
 | 
			
		||||
 | 
			
		||||
        attrs['_is_document'] = attrs.get('_is_document', False)
 | 
			
		||||
        attrs['_cached_reference_fields'] = []
 | 
			
		||||
        attrs["_is_document"] = attrs.get("_is_document", False)
 | 
			
		||||
        attrs["_cached_reference_fields"] = []
 | 
			
		||||
 | 
			
		||||
        # EmbeddedDocuments could have meta data for inheritance
 | 
			
		||||
        if 'meta' in attrs:
 | 
			
		||||
            attrs['_meta'] = attrs.pop('meta')
 | 
			
		||||
        if "meta" in attrs:
 | 
			
		||||
            attrs["_meta"] = attrs.pop("meta")
 | 
			
		||||
 | 
			
		||||
        # EmbeddedDocuments should inherit meta data
 | 
			
		||||
        if '_meta' not in attrs:
 | 
			
		||||
        if "_meta" not in attrs:
 | 
			
		||||
            meta = MetaDict()
 | 
			
		||||
            for base in flattened_bases[::-1]:
 | 
			
		||||
                # Add any mixin metadata from plain objects
 | 
			
		||||
                if hasattr(base, 'meta'):
 | 
			
		||||
                if hasattr(base, "meta"):
 | 
			
		||||
                    meta.merge(base.meta)
 | 
			
		||||
                elif hasattr(base, '_meta'):
 | 
			
		||||
                elif hasattr(base, "_meta"):
 | 
			
		||||
                    meta.merge(base._meta)
 | 
			
		||||
            attrs['_meta'] = meta
 | 
			
		||||
            attrs['_meta']['abstract'] = False  # 789: EmbeddedDocument shouldn't inherit abstract
 | 
			
		||||
            attrs["_meta"] = meta
 | 
			
		||||
            attrs["_meta"][
 | 
			
		||||
                "abstract"
 | 
			
		||||
            ] = False  # 789: EmbeddedDocument shouldn't inherit abstract
 | 
			
		||||
 | 
			
		||||
        # If allow_inheritance is True, add a "_cls" string field to the attrs
 | 
			
		||||
        if attrs['_meta'].get('allow_inheritance'):
 | 
			
		||||
            StringField = _import_class('StringField')
 | 
			
		||||
            attrs['_cls'] = StringField()
 | 
			
		||||
        if attrs["_meta"].get("allow_inheritance"):
 | 
			
		||||
            StringField = _import_class("StringField")
 | 
			
		||||
            attrs["_cls"] = StringField()
 | 
			
		||||
 | 
			
		||||
        # Handle document Fields
 | 
			
		||||
 | 
			
		||||
        # Merge all fields from subclasses
 | 
			
		||||
        doc_fields = {}
 | 
			
		||||
        for base in flattened_bases[::-1]:
 | 
			
		||||
            if hasattr(base, '_fields'):
 | 
			
		||||
            if hasattr(base, "_fields"):
 | 
			
		||||
                doc_fields.update(base._fields)
 | 
			
		||||
 | 
			
		||||
            # Standard object mixin - merge in any Fields
 | 
			
		||||
            if not hasattr(base, '_meta'):
 | 
			
		||||
            if not hasattr(base, "_meta"):
 | 
			
		||||
                base_fields = {}
 | 
			
		||||
                for attr_name, attr_value in iteritems(base.__dict__):
 | 
			
		||||
                    if not isinstance(attr_value, BaseField):
 | 
			
		||||
@@ -85,27 +90,31 @@ class DocumentMetaclass(type):
 | 
			
		||||
            doc_fields[attr_name] = attr_value
 | 
			
		||||
 | 
			
		||||
            # Count names to ensure no db_field redefinitions
 | 
			
		||||
            field_names[attr_value.db_field] = field_names.get(
 | 
			
		||||
                attr_value.db_field, 0) + 1
 | 
			
		||||
            field_names[attr_value.db_field] = (
 | 
			
		||||
                field_names.get(attr_value.db_field, 0) + 1
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        # Ensure no duplicate db_fields
 | 
			
		||||
        duplicate_db_fields = [k for k, v in field_names.items() if v > 1]
 | 
			
		||||
        if duplicate_db_fields:
 | 
			
		||||
            msg = ('Multiple db_fields defined for: %s ' %
 | 
			
		||||
                   ', '.join(duplicate_db_fields))
 | 
			
		||||
            msg = "Multiple db_fields defined for: %s " % ", ".join(duplicate_db_fields)
 | 
			
		||||
            raise InvalidDocumentError(msg)
 | 
			
		||||
 | 
			
		||||
        # Set _fields and db_field maps
 | 
			
		||||
        attrs['_fields'] = doc_fields
 | 
			
		||||
        attrs['_db_field_map'] = {k: getattr(v, 'db_field', k)
 | 
			
		||||
                                  for k, v in doc_fields.items()}
 | 
			
		||||
        attrs['_reverse_db_field_map'] = {
 | 
			
		||||
            v: k for k, v in attrs['_db_field_map'].items()
 | 
			
		||||
        attrs["_fields"] = doc_fields
 | 
			
		||||
        attrs["_db_field_map"] = {
 | 
			
		||||
            k: getattr(v, "db_field", k) for k, v in doc_fields.items()
 | 
			
		||||
        }
 | 
			
		||||
        attrs["_reverse_db_field_map"] = {
 | 
			
		||||
            v: k for k, v in attrs["_db_field_map"].items()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        attrs['_fields_ordered'] = tuple(i[1] for i in sorted(
 | 
			
		||||
                                         (v.creation_counter, v.name)
 | 
			
		||||
                                         for v in itervalues(doc_fields)))
 | 
			
		||||
        attrs["_fields_ordered"] = tuple(
 | 
			
		||||
            i[1]
 | 
			
		||||
            for i in sorted(
 | 
			
		||||
                (v.creation_counter, v.name) for v in itervalues(doc_fields)
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        #
 | 
			
		||||
        # Set document hierarchy
 | 
			
		||||
@@ -113,32 +122,34 @@ class DocumentMetaclass(type):
 | 
			
		||||
        superclasses = ()
 | 
			
		||||
        class_name = [name]
 | 
			
		||||
        for base in flattened_bases:
 | 
			
		||||
            if (not getattr(base, '_is_base_cls', True) and
 | 
			
		||||
                    not getattr(base, '_meta', {}).get('abstract', True)):
 | 
			
		||||
            if not getattr(base, "_is_base_cls", True) and not getattr(
 | 
			
		||||
                base, "_meta", {}
 | 
			
		||||
            ).get("abstract", True):
 | 
			
		||||
                # Collate hierarchy for _cls and _subclasses
 | 
			
		||||
                class_name.append(base.__name__)
 | 
			
		||||
 | 
			
		||||
            if hasattr(base, '_meta'):
 | 
			
		||||
            if hasattr(base, "_meta"):
 | 
			
		||||
                # Warn if allow_inheritance isn't set and prevent
 | 
			
		||||
                # inheritance of classes where inheritance is set to False
 | 
			
		||||
                allow_inheritance = base._meta.get('allow_inheritance')
 | 
			
		||||
                if not allow_inheritance and not base._meta.get('abstract'):
 | 
			
		||||
                    raise ValueError('Document %s may not be subclassed. '
 | 
			
		||||
                                     'To enable inheritance, use the "allow_inheritance" meta attribute.' %
 | 
			
		||||
                                     base.__name__)
 | 
			
		||||
                allow_inheritance = base._meta.get("allow_inheritance")
 | 
			
		||||
                if not allow_inheritance and not base._meta.get("abstract"):
 | 
			
		||||
                    raise ValueError(
 | 
			
		||||
                        "Document %s may not be subclassed. "
 | 
			
		||||
                        'To enable inheritance, use the "allow_inheritance" meta attribute.'
 | 
			
		||||
                        % base.__name__
 | 
			
		||||
                    )
 | 
			
		||||
 | 
			
		||||
        # Get superclasses from last base superclass
 | 
			
		||||
        document_bases = [b for b in flattened_bases
 | 
			
		||||
                          if hasattr(b, '_class_name')]
 | 
			
		||||
        document_bases = [b for b in flattened_bases if hasattr(b, "_class_name")]
 | 
			
		||||
        if document_bases:
 | 
			
		||||
            superclasses = document_bases[0]._superclasses
 | 
			
		||||
            superclasses += (document_bases[0]._class_name, )
 | 
			
		||||
            superclasses += (document_bases[0]._class_name,)
 | 
			
		||||
 | 
			
		||||
        _cls = '.'.join(reversed(class_name))
 | 
			
		||||
        attrs['_class_name'] = _cls
 | 
			
		||||
        attrs['_superclasses'] = superclasses
 | 
			
		||||
        attrs['_subclasses'] = (_cls, )
 | 
			
		||||
        attrs['_types'] = attrs['_subclasses']  # TODO depreciate _types
 | 
			
		||||
        _cls = ".".join(reversed(class_name))
 | 
			
		||||
        attrs["_class_name"] = _cls
 | 
			
		||||
        attrs["_superclasses"] = superclasses
 | 
			
		||||
        attrs["_subclasses"] = (_cls,)
 | 
			
		||||
        attrs["_types"] = attrs["_subclasses"]  # TODO depreciate _types
 | 
			
		||||
 | 
			
		||||
        # Create the new_class
 | 
			
		||||
        new_class = super_new(mcs, name, bases, attrs)
 | 
			
		||||
@@ -149,8 +160,12 @@ class DocumentMetaclass(type):
 | 
			
		||||
                base._subclasses += (_cls,)
 | 
			
		||||
            base._types = base._subclasses  # TODO depreciate _types
 | 
			
		||||
 | 
			
		||||
        (Document, EmbeddedDocument, DictField,
 | 
			
		||||
         CachedReferenceField) = mcs._import_classes()
 | 
			
		||||
        (
 | 
			
		||||
            Document,
 | 
			
		||||
            EmbeddedDocument,
 | 
			
		||||
            DictField,
 | 
			
		||||
            CachedReferenceField,
 | 
			
		||||
        ) = mcs._import_classes()
 | 
			
		||||
 | 
			
		||||
        if issubclass(new_class, Document):
 | 
			
		||||
            new_class._collection = None
 | 
			
		||||
@@ -169,52 +184,55 @@ class DocumentMetaclass(type):
 | 
			
		||||
            for val in new_class.__dict__.values():
 | 
			
		||||
                if isinstance(val, classmethod):
 | 
			
		||||
                    f = val.__get__(new_class)
 | 
			
		||||
                    if hasattr(f, '__func__') and not hasattr(f, 'im_func'):
 | 
			
		||||
                        f.__dict__.update({'im_func': getattr(f, '__func__')})
 | 
			
		||||
                    if hasattr(f, '__self__') and not hasattr(f, 'im_self'):
 | 
			
		||||
                        f.__dict__.update({'im_self': getattr(f, '__self__')})
 | 
			
		||||
                    if hasattr(f, "__func__") and not hasattr(f, "im_func"):
 | 
			
		||||
                        f.__dict__.update({"im_func": getattr(f, "__func__")})
 | 
			
		||||
                    if hasattr(f, "__self__") and not hasattr(f, "im_self"):
 | 
			
		||||
                        f.__dict__.update({"im_self": getattr(f, "__self__")})
 | 
			
		||||
 | 
			
		||||
        # Handle delete rules
 | 
			
		||||
        for field in itervalues(new_class._fields):
 | 
			
		||||
            f = field
 | 
			
		||||
            if f.owner_document is None:
 | 
			
		||||
                f.owner_document = new_class
 | 
			
		||||
            delete_rule = getattr(f, 'reverse_delete_rule', DO_NOTHING)
 | 
			
		||||
            delete_rule = getattr(f, "reverse_delete_rule", DO_NOTHING)
 | 
			
		||||
            if isinstance(f, CachedReferenceField):
 | 
			
		||||
 | 
			
		||||
                if issubclass(new_class, EmbeddedDocument):
 | 
			
		||||
                    raise InvalidDocumentError('CachedReferenceFields is not '
 | 
			
		||||
                                               'allowed in EmbeddedDocuments')
 | 
			
		||||
                    raise InvalidDocumentError(
 | 
			
		||||
                        "CachedReferenceFields is not allowed in EmbeddedDocuments"
 | 
			
		||||
                    )
 | 
			
		||||
 | 
			
		||||
                if f.auto_sync:
 | 
			
		||||
                    f.start_listener()
 | 
			
		||||
 | 
			
		||||
                f.document_type._cached_reference_fields.append(f)
 | 
			
		||||
 | 
			
		||||
            if isinstance(f, ComplexBaseField) and hasattr(f, 'field'):
 | 
			
		||||
                delete_rule = getattr(f.field,
 | 
			
		||||
                                      'reverse_delete_rule',
 | 
			
		||||
                                      DO_NOTHING)
 | 
			
		||||
            if isinstance(f, ComplexBaseField) and hasattr(f, "field"):
 | 
			
		||||
                delete_rule = getattr(f.field, "reverse_delete_rule", DO_NOTHING)
 | 
			
		||||
                if isinstance(f, DictField) and delete_rule != DO_NOTHING:
 | 
			
		||||
                    msg = ('Reverse delete rules are not supported '
 | 
			
		||||
                           'for %s (field: %s)' %
 | 
			
		||||
                           (field.__class__.__name__, field.name))
 | 
			
		||||
                    msg = (
 | 
			
		||||
                        "Reverse delete rules are not supported "
 | 
			
		||||
                        "for %s (field: %s)" % (field.__class__.__name__, field.name)
 | 
			
		||||
                    )
 | 
			
		||||
                    raise InvalidDocumentError(msg)
 | 
			
		||||
 | 
			
		||||
                f = field.field
 | 
			
		||||
 | 
			
		||||
            if delete_rule != DO_NOTHING:
 | 
			
		||||
                if issubclass(new_class, EmbeddedDocument):
 | 
			
		||||
                    msg = ('Reverse delete rules are not supported for '
 | 
			
		||||
                           'EmbeddedDocuments (field: %s)' % field.name)
 | 
			
		||||
                    msg = (
 | 
			
		||||
                        "Reverse delete rules are not supported for "
 | 
			
		||||
                        "EmbeddedDocuments (field: %s)" % field.name
 | 
			
		||||
                    )
 | 
			
		||||
                    raise InvalidDocumentError(msg)
 | 
			
		||||
                f.document_type.register_delete_rule(new_class,
 | 
			
		||||
                                                     field.name, delete_rule)
 | 
			
		||||
                f.document_type.register_delete_rule(new_class, field.name, delete_rule)
 | 
			
		||||
 | 
			
		||||
            if (field.name and hasattr(Document, field.name) and
 | 
			
		||||
                    EmbeddedDocument not in new_class.mro()):
 | 
			
		||||
                msg = ('%s is a document method and not a valid '
 | 
			
		||||
                       'field name' % field.name)
 | 
			
		||||
            if (
 | 
			
		||||
                field.name
 | 
			
		||||
                and hasattr(Document, field.name)
 | 
			
		||||
                and EmbeddedDocument not in new_class.mro()
 | 
			
		||||
            ):
 | 
			
		||||
                msg = "%s is a document method and not a valid field name" % field.name
 | 
			
		||||
                raise InvalidDocumentError(msg)
 | 
			
		||||
 | 
			
		||||
        return new_class
 | 
			
		||||
@@ -239,10 +257,10 @@ class DocumentMetaclass(type):
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def _import_classes(mcs):
 | 
			
		||||
        Document = _import_class('Document')
 | 
			
		||||
        EmbeddedDocument = _import_class('EmbeddedDocument')
 | 
			
		||||
        DictField = _import_class('DictField')
 | 
			
		||||
        CachedReferenceField = _import_class('CachedReferenceField')
 | 
			
		||||
        Document = _import_class("Document")
 | 
			
		||||
        EmbeddedDocument = _import_class("EmbeddedDocument")
 | 
			
		||||
        DictField = _import_class("DictField")
 | 
			
		||||
        CachedReferenceField = _import_class("CachedReferenceField")
 | 
			
		||||
        return Document, EmbeddedDocument, DictField, CachedReferenceField
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -256,65 +274,67 @@ class TopLevelDocumentMetaclass(DocumentMetaclass):
 | 
			
		||||
        super_new = super(TopLevelDocumentMetaclass, mcs).__new__
 | 
			
		||||
 | 
			
		||||
        # Set default _meta data if base class, otherwise get user defined meta
 | 
			
		||||
        if attrs.get('my_metaclass') == TopLevelDocumentMetaclass:
 | 
			
		||||
        if attrs.get("my_metaclass") == TopLevelDocumentMetaclass:
 | 
			
		||||
            # defaults
 | 
			
		||||
            attrs['_meta'] = {
 | 
			
		||||
                'abstract': True,
 | 
			
		||||
                'max_documents': None,
 | 
			
		||||
                'max_size': None,
 | 
			
		||||
                'ordering': [],  # default ordering applied at runtime
 | 
			
		||||
                'indexes': [],  # indexes to be ensured at runtime
 | 
			
		||||
                'id_field': None,
 | 
			
		||||
                'index_background': False,
 | 
			
		||||
                'index_drop_dups': False,
 | 
			
		||||
                'index_opts': None,
 | 
			
		||||
                'delete_rules': None,
 | 
			
		||||
 | 
			
		||||
            attrs["_meta"] = {
 | 
			
		||||
                "abstract": True,
 | 
			
		||||
                "max_documents": None,
 | 
			
		||||
                "max_size": None,
 | 
			
		||||
                "ordering": [],  # default ordering applied at runtime
 | 
			
		||||
                "indexes": [],  # indexes to be ensured at runtime
 | 
			
		||||
                "id_field": None,
 | 
			
		||||
                "index_background": False,
 | 
			
		||||
                "index_drop_dups": False,
 | 
			
		||||
                "index_opts": None,
 | 
			
		||||
                "delete_rules": None,
 | 
			
		||||
                # allow_inheritance can be True, False, and None. True means
 | 
			
		||||
                # "allow inheritance", False means "don't allow inheritance",
 | 
			
		||||
                # None means "do whatever your parent does, or don't allow
 | 
			
		||||
                # inheritance if you're a top-level class".
 | 
			
		||||
                'allow_inheritance': None,
 | 
			
		||||
                "allow_inheritance": None,
 | 
			
		||||
            }
 | 
			
		||||
            attrs['_is_base_cls'] = True
 | 
			
		||||
            attrs['_meta'].update(attrs.get('meta', {}))
 | 
			
		||||
            attrs["_is_base_cls"] = True
 | 
			
		||||
            attrs["_meta"].update(attrs.get("meta", {}))
 | 
			
		||||
        else:
 | 
			
		||||
            attrs['_meta'] = attrs.get('meta', {})
 | 
			
		||||
            attrs["_meta"] = attrs.get("meta", {})
 | 
			
		||||
            # Explicitly set abstract to false unless set
 | 
			
		||||
            attrs['_meta']['abstract'] = attrs['_meta'].get('abstract', False)
 | 
			
		||||
            attrs['_is_base_cls'] = False
 | 
			
		||||
            attrs["_meta"]["abstract"] = attrs["_meta"].get("abstract", False)
 | 
			
		||||
            attrs["_is_base_cls"] = False
 | 
			
		||||
 | 
			
		||||
        # Set flag marking as document class - as opposed to an object mixin
 | 
			
		||||
        attrs['_is_document'] = True
 | 
			
		||||
        attrs["_is_document"] = True
 | 
			
		||||
 | 
			
		||||
        # Ensure queryset_class is inherited
 | 
			
		||||
        if 'objects' in attrs:
 | 
			
		||||
            manager = attrs['objects']
 | 
			
		||||
            if hasattr(manager, 'queryset_class'):
 | 
			
		||||
                attrs['_meta']['queryset_class'] = manager.queryset_class
 | 
			
		||||
        if "objects" in attrs:
 | 
			
		||||
            manager = attrs["objects"]
 | 
			
		||||
            if hasattr(manager, "queryset_class"):
 | 
			
		||||
                attrs["_meta"]["queryset_class"] = manager.queryset_class
 | 
			
		||||
 | 
			
		||||
        # Clean up top level meta
 | 
			
		||||
        if 'meta' in attrs:
 | 
			
		||||
            del attrs['meta']
 | 
			
		||||
        if "meta" in attrs:
 | 
			
		||||
            del attrs["meta"]
 | 
			
		||||
 | 
			
		||||
        # Find the parent document class
 | 
			
		||||
        parent_doc_cls = [b for b in flattened_bases
 | 
			
		||||
                          if b.__class__ == TopLevelDocumentMetaclass]
 | 
			
		||||
        parent_doc_cls = [
 | 
			
		||||
            b for b in flattened_bases if b.__class__ == TopLevelDocumentMetaclass
 | 
			
		||||
        ]
 | 
			
		||||
        parent_doc_cls = None if not parent_doc_cls else parent_doc_cls[0]
 | 
			
		||||
 | 
			
		||||
        # Prevent classes setting collection different to their parents
 | 
			
		||||
        # If parent wasn't an abstract class
 | 
			
		||||
        if (parent_doc_cls and 'collection' in attrs.get('_meta', {}) and
 | 
			
		||||
                not parent_doc_cls._meta.get('abstract', True)):
 | 
			
		||||
            msg = 'Trying to set a collection on a subclass (%s)' % name
 | 
			
		||||
        if (
 | 
			
		||||
            parent_doc_cls
 | 
			
		||||
            and "collection" in attrs.get("_meta", {})
 | 
			
		||||
            and not parent_doc_cls._meta.get("abstract", True)
 | 
			
		||||
        ):
 | 
			
		||||
            msg = "Trying to set a collection on a subclass (%s)" % name
 | 
			
		||||
            warnings.warn(msg, SyntaxWarning)
 | 
			
		||||
            del attrs['_meta']['collection']
 | 
			
		||||
            del attrs["_meta"]["collection"]
 | 
			
		||||
 | 
			
		||||
        # Ensure abstract documents have abstract bases
 | 
			
		||||
        if attrs.get('_is_base_cls') or attrs['_meta'].get('abstract'):
 | 
			
		||||
            if (parent_doc_cls and
 | 
			
		||||
                    not parent_doc_cls._meta.get('abstract', False)):
 | 
			
		||||
                msg = 'Abstract document cannot have non-abstract base'
 | 
			
		||||
        if attrs.get("_is_base_cls") or attrs["_meta"].get("abstract"):
 | 
			
		||||
            if parent_doc_cls and not parent_doc_cls._meta.get("abstract", False):
 | 
			
		||||
                msg = "Abstract document cannot have non-abstract base"
 | 
			
		||||
                raise ValueError(msg)
 | 
			
		||||
            return super_new(mcs, name, bases, attrs)
 | 
			
		||||
 | 
			
		||||
@@ -323,38 +343,43 @@ class TopLevelDocumentMetaclass(DocumentMetaclass):
 | 
			
		||||
        meta = MetaDict()
 | 
			
		||||
        for base in flattened_bases[::-1]:
 | 
			
		||||
            # Add any mixin metadata from plain objects
 | 
			
		||||
            if hasattr(base, 'meta'):
 | 
			
		||||
            if hasattr(base, "meta"):
 | 
			
		||||
                meta.merge(base.meta)
 | 
			
		||||
            elif hasattr(base, '_meta'):
 | 
			
		||||
            elif hasattr(base, "_meta"):
 | 
			
		||||
                meta.merge(base._meta)
 | 
			
		||||
 | 
			
		||||
            # Set collection in the meta if its callable
 | 
			
		||||
            if (getattr(base, '_is_document', False) and
 | 
			
		||||
                    not base._meta.get('abstract')):
 | 
			
		||||
                collection = meta.get('collection', None)
 | 
			
		||||
            if getattr(base, "_is_document", False) and not base._meta.get("abstract"):
 | 
			
		||||
                collection = meta.get("collection", None)
 | 
			
		||||
                if callable(collection):
 | 
			
		||||
                    meta['collection'] = collection(base)
 | 
			
		||||
                    meta["collection"] = collection(base)
 | 
			
		||||
 | 
			
		||||
        meta.merge(attrs.get('_meta', {}))  # Top level meta
 | 
			
		||||
        meta.merge(attrs.get("_meta", {}))  # Top level meta
 | 
			
		||||
 | 
			
		||||
        # Only simple classes (i.e. direct subclasses of Document) may set
 | 
			
		||||
        # allow_inheritance to False. If the base Document allows inheritance,
 | 
			
		||||
        # none of its subclasses can override allow_inheritance to False.
 | 
			
		||||
        simple_class = all([b._meta.get('abstract')
 | 
			
		||||
                            for b in flattened_bases if hasattr(b, '_meta')])
 | 
			
		||||
        simple_class = all(
 | 
			
		||||
            [b._meta.get("abstract") for b in flattened_bases if hasattr(b, "_meta")]
 | 
			
		||||
        )
 | 
			
		||||
        if (
 | 
			
		||||
            not simple_class and
 | 
			
		||||
            meta['allow_inheritance'] is False and
 | 
			
		||||
            not meta['abstract']
 | 
			
		||||
            not simple_class
 | 
			
		||||
            and meta["allow_inheritance"] is False
 | 
			
		||||
            and not meta["abstract"]
 | 
			
		||||
        ):
 | 
			
		||||
            raise ValueError('Only direct subclasses of Document may set '
 | 
			
		||||
                             '"allow_inheritance" to False')
 | 
			
		||||
            raise ValueError(
 | 
			
		||||
                "Only direct subclasses of Document may set "
 | 
			
		||||
                '"allow_inheritance" to False'
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        # Set default collection name
 | 
			
		||||
        if 'collection' not in meta:
 | 
			
		||||
            meta['collection'] = ''.join('_%s' % c if c.isupper() else c
 | 
			
		||||
                                         for c in name).strip('_').lower()
 | 
			
		||||
        attrs['_meta'] = meta
 | 
			
		||||
        if "collection" not in meta:
 | 
			
		||||
            meta["collection"] = (
 | 
			
		||||
                "".join("_%s" % c if c.isupper() else c for c in name)
 | 
			
		||||
                .strip("_")
 | 
			
		||||
                .lower()
 | 
			
		||||
            )
 | 
			
		||||
        attrs["_meta"] = meta
 | 
			
		||||
 | 
			
		||||
        # Call super and get the new class
 | 
			
		||||
        new_class = super_new(mcs, name, bases, attrs)
 | 
			
		||||
@@ -362,36 +387,36 @@ class TopLevelDocumentMetaclass(DocumentMetaclass):
 | 
			
		||||
        meta = new_class._meta
 | 
			
		||||
 | 
			
		||||
        # Set index specifications
 | 
			
		||||
        meta['index_specs'] = new_class._build_index_specs(meta['indexes'])
 | 
			
		||||
        meta["index_specs"] = new_class._build_index_specs(meta["indexes"])
 | 
			
		||||
 | 
			
		||||
        # If collection is a callable - call it and set the value
 | 
			
		||||
        collection = meta.get('collection')
 | 
			
		||||
        collection = meta.get("collection")
 | 
			
		||||
        if callable(collection):
 | 
			
		||||
            new_class._meta['collection'] = collection(new_class)
 | 
			
		||||
            new_class._meta["collection"] = collection(new_class)
 | 
			
		||||
 | 
			
		||||
        # Provide a default queryset unless exists or one has been set
 | 
			
		||||
        if 'objects' not in dir(new_class):
 | 
			
		||||
        if "objects" not in dir(new_class):
 | 
			
		||||
            new_class.objects = QuerySetManager()
 | 
			
		||||
 | 
			
		||||
        # Validate the fields and set primary key if needed
 | 
			
		||||
        for field_name, field in iteritems(new_class._fields):
 | 
			
		||||
            if field.primary_key:
 | 
			
		||||
                # Ensure only one primary key is set
 | 
			
		||||
                current_pk = new_class._meta.get('id_field')
 | 
			
		||||
                current_pk = new_class._meta.get("id_field")
 | 
			
		||||
                if current_pk and current_pk != field_name:
 | 
			
		||||
                    raise ValueError('Cannot override primary key field')
 | 
			
		||||
                    raise ValueError("Cannot override primary key field")
 | 
			
		||||
 | 
			
		||||
                # Set primary key
 | 
			
		||||
                if not current_pk:
 | 
			
		||||
                    new_class._meta['id_field'] = field_name
 | 
			
		||||
                    new_class._meta["id_field"] = field_name
 | 
			
		||||
                    new_class.id = field
 | 
			
		||||
 | 
			
		||||
        # If the document doesn't explicitly define a primary key field, create
 | 
			
		||||
        # one. Make it an ObjectIdField and give it a non-clashing name ("id"
 | 
			
		||||
        # by default, but can be different if that one's taken).
 | 
			
		||||
        if not new_class._meta.get('id_field'):
 | 
			
		||||
        if not new_class._meta.get("id_field"):
 | 
			
		||||
            id_name, id_db_name = mcs.get_auto_id_names(new_class)
 | 
			
		||||
            new_class._meta['id_field'] = id_name
 | 
			
		||||
            new_class._meta["id_field"] = id_name
 | 
			
		||||
            new_class._fields[id_name] = ObjectIdField(db_field=id_db_name)
 | 
			
		||||
            new_class._fields[id_name].name = id_name
 | 
			
		||||
            new_class.id = new_class._fields[id_name]
 | 
			
		||||
@@ -400,22 +425,20 @@ class TopLevelDocumentMetaclass(DocumentMetaclass):
 | 
			
		||||
 | 
			
		||||
            # Prepend the ID field to _fields_ordered (so that it's *always*
 | 
			
		||||
            # the first field).
 | 
			
		||||
            new_class._fields_ordered = (id_name, ) + new_class._fields_ordered
 | 
			
		||||
            new_class._fields_ordered = (id_name,) + new_class._fields_ordered
 | 
			
		||||
 | 
			
		||||
        # Merge in exceptions with parent hierarchy.
 | 
			
		||||
        exceptions_to_merge = (DoesNotExist, MultipleObjectsReturned)
 | 
			
		||||
        module = attrs.get('__module__')
 | 
			
		||||
        module = attrs.get("__module__")
 | 
			
		||||
        for exc in exceptions_to_merge:
 | 
			
		||||
            name = exc.__name__
 | 
			
		||||
            parents = tuple(
 | 
			
		||||
                getattr(base, name)
 | 
			
		||||
                for base in flattened_bases
 | 
			
		||||
                if hasattr(base, name)
 | 
			
		||||
                getattr(base, name) for base in flattened_bases if hasattr(base, name)
 | 
			
		||||
            ) or (exc,)
 | 
			
		||||
 | 
			
		||||
            # Create a new exception and set it as an attribute on the new
 | 
			
		||||
            # class.
 | 
			
		||||
            exception = type(name, parents, {'__module__': module})
 | 
			
		||||
            exception = type(name, parents, {"__module__": module})
 | 
			
		||||
            setattr(new_class, name, exception)
 | 
			
		||||
 | 
			
		||||
        return new_class
 | 
			
		||||
@@ -431,23 +454,17 @@ class TopLevelDocumentMetaclass(DocumentMetaclass):
 | 
			
		||||
        Defaults to ('id', '_id'), or generates a non-clashing name in the form
 | 
			
		||||
        of ('auto_id_X', '_auto_id_X') if the default name is already taken.
 | 
			
		||||
        """
 | 
			
		||||
        id_name, id_db_name = ('id', '_id')
 | 
			
		||||
        id_name, id_db_name = ("id", "_id")
 | 
			
		||||
        existing_fields = {field_name for field_name in new_class._fields}
 | 
			
		||||
        existing_db_fields = {v.db_field for v in new_class._fields.values()}
 | 
			
		||||
        if (
 | 
			
		||||
            id_name not in existing_fields and
 | 
			
		||||
            id_db_name not in existing_db_fields
 | 
			
		||||
        ):
 | 
			
		||||
        if id_name not in existing_fields and id_db_name not in existing_db_fields:
 | 
			
		||||
            return id_name, id_db_name
 | 
			
		||||
 | 
			
		||||
        id_basename, id_db_basename, i = ('auto_id', '_auto_id', 0)
 | 
			
		||||
        id_basename, id_db_basename, i = ("auto_id", "_auto_id", 0)
 | 
			
		||||
        for i in itertools.count():
 | 
			
		||||
            id_name = '{0}_{1}'.format(id_basename, i)
 | 
			
		||||
            id_db_name = '{0}_{1}'.format(id_db_basename, i)
 | 
			
		||||
            if (
 | 
			
		||||
                id_name not in existing_fields and
 | 
			
		||||
                id_db_name not in existing_db_fields
 | 
			
		||||
            ):
 | 
			
		||||
            id_name = "{0}_{1}".format(id_basename, i)
 | 
			
		||||
            id_db_name = "{0}_{1}".format(id_db_basename, i)
 | 
			
		||||
            if id_name not in existing_fields and id_db_name not in existing_db_fields:
 | 
			
		||||
                return id_name, id_db_name
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -455,7 +472,8 @@ class MetaDict(dict):
 | 
			
		||||
    """Custom dictionary for meta classes.
 | 
			
		||||
    Handles the merging of set indexes
 | 
			
		||||
    """
 | 
			
		||||
    _merge_options = ('indexes',)
 | 
			
		||||
 | 
			
		||||
    _merge_options = ("indexes",)
 | 
			
		||||
 | 
			
		||||
    def merge(self, new_options):
 | 
			
		||||
        for k, v in iteritems(new_options):
 | 
			
		||||
@@ -467,4 +485,5 @@ class MetaDict(dict):
 | 
			
		||||
 | 
			
		||||
class BasesTuple(tuple):
 | 
			
		||||
    """Special class to handle introspection of bases tuple in __new__"""
 | 
			
		||||
 | 
			
		||||
    pass
 | 
			
		||||
 
 | 
			
		||||
@@ -19,34 +19,44 @@ def _import_class(cls_name):
 | 
			
		||||
    if cls_name in _class_registry_cache:
 | 
			
		||||
        return _class_registry_cache.get(cls_name)
 | 
			
		||||
 | 
			
		||||
    doc_classes = ('Document', 'DynamicEmbeddedDocument', 'EmbeddedDocument',
 | 
			
		||||
                   'MapReduceDocument')
 | 
			
		||||
    doc_classes = (
 | 
			
		||||
        "Document",
 | 
			
		||||
        "DynamicEmbeddedDocument",
 | 
			
		||||
        "EmbeddedDocument",
 | 
			
		||||
        "MapReduceDocument",
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    # Field Classes
 | 
			
		||||
    if not _field_list_cache:
 | 
			
		||||
        from mongoengine.fields import __all__ as fields
 | 
			
		||||
 | 
			
		||||
        _field_list_cache.extend(fields)
 | 
			
		||||
        from mongoengine.base.fields import __all__ as fields
 | 
			
		||||
 | 
			
		||||
        _field_list_cache.extend(fields)
 | 
			
		||||
 | 
			
		||||
    field_classes = _field_list_cache
 | 
			
		||||
 | 
			
		||||
    deref_classes = ('DeReference',)
 | 
			
		||||
    deref_classes = ("DeReference",)
 | 
			
		||||
 | 
			
		||||
    if cls_name == 'BaseDocument':
 | 
			
		||||
    if cls_name == "BaseDocument":
 | 
			
		||||
        from mongoengine.base import document as module
 | 
			
		||||
        import_classes = ['BaseDocument']
 | 
			
		||||
 | 
			
		||||
        import_classes = ["BaseDocument"]
 | 
			
		||||
    elif cls_name in doc_classes:
 | 
			
		||||
        from mongoengine import document as module
 | 
			
		||||
 | 
			
		||||
        import_classes = doc_classes
 | 
			
		||||
    elif cls_name in field_classes:
 | 
			
		||||
        from mongoengine import fields as module
 | 
			
		||||
 | 
			
		||||
        import_classes = field_classes
 | 
			
		||||
    elif cls_name in deref_classes:
 | 
			
		||||
        from mongoengine import dereference as module
 | 
			
		||||
 | 
			
		||||
        import_classes = deref_classes
 | 
			
		||||
    else:
 | 
			
		||||
        raise ValueError('No import set for: %s' % cls_name)
 | 
			
		||||
        raise ValueError("No import set for: %s" % cls_name)
 | 
			
		||||
 | 
			
		||||
    for cls in import_classes:
 | 
			
		||||
        _class_registry_cache[cls] = getattr(module, cls)
 | 
			
		||||
 
 | 
			
		||||
@@ -3,21 +3,21 @@ from pymongo.database import _check_name
 | 
			
		||||
import six
 | 
			
		||||
 | 
			
		||||
__all__ = [
 | 
			
		||||
    'DEFAULT_CONNECTION_NAME',
 | 
			
		||||
    'DEFAULT_DATABASE_NAME',
 | 
			
		||||
    'MongoEngineConnectionError',
 | 
			
		||||
    'connect',
 | 
			
		||||
    'disconnect',
 | 
			
		||||
    'disconnect_all',
 | 
			
		||||
    'get_connection',
 | 
			
		||||
    'get_db',
 | 
			
		||||
    'register_connection',
 | 
			
		||||
    "DEFAULT_CONNECTION_NAME",
 | 
			
		||||
    "DEFAULT_DATABASE_NAME",
 | 
			
		||||
    "MongoEngineConnectionError",
 | 
			
		||||
    "connect",
 | 
			
		||||
    "disconnect",
 | 
			
		||||
    "disconnect_all",
 | 
			
		||||
    "get_connection",
 | 
			
		||||
    "get_db",
 | 
			
		||||
    "register_connection",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DEFAULT_CONNECTION_NAME = 'default'
 | 
			
		||||
DEFAULT_DATABASE_NAME = 'test'
 | 
			
		||||
DEFAULT_HOST = 'localhost'
 | 
			
		||||
DEFAULT_CONNECTION_NAME = "default"
 | 
			
		||||
DEFAULT_DATABASE_NAME = "test"
 | 
			
		||||
DEFAULT_HOST = "localhost"
 | 
			
		||||
DEFAULT_PORT = 27017
 | 
			
		||||
 | 
			
		||||
_connection_settings = {}
 | 
			
		||||
@@ -31,6 +31,7 @@ class MongoEngineConnectionError(Exception):
 | 
			
		||||
    """Error raised when the database connection can't be established or
 | 
			
		||||
    when a connection with a requested alias can't be retrieved.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -39,18 +40,23 @@ def _check_db_name(name):
 | 
			
		||||
    This functionality is copied from pymongo Database class constructor.
 | 
			
		||||
    """
 | 
			
		||||
    if not isinstance(name, six.string_types):
 | 
			
		||||
        raise TypeError('name must be an instance of %s' % six.string_types)
 | 
			
		||||
    elif name != '$external':
 | 
			
		||||
        raise TypeError("name must be an instance of %s" % six.string_types)
 | 
			
		||||
    elif name != "$external":
 | 
			
		||||
        _check_name(name)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _get_connection_settings(
 | 
			
		||||
        db=None, name=None, host=None, port=None,
 | 
			
		||||
        read_preference=READ_PREFERENCE,
 | 
			
		||||
        username=None, password=None,
 | 
			
		||||
        authentication_source=None,
 | 
			
		||||
        authentication_mechanism=None,
 | 
			
		||||
        **kwargs):
 | 
			
		||||
    db=None,
 | 
			
		||||
    name=None,
 | 
			
		||||
    host=None,
 | 
			
		||||
    port=None,
 | 
			
		||||
    read_preference=READ_PREFERENCE,
 | 
			
		||||
    username=None,
 | 
			
		||||
    password=None,
 | 
			
		||||
    authentication_source=None,
 | 
			
		||||
    authentication_mechanism=None,
 | 
			
		||||
    **kwargs
 | 
			
		||||
):
 | 
			
		||||
    """Get the connection settings as a dict
 | 
			
		||||
 | 
			
		||||
    : param db: the name of the database to use, for compatibility with connect
 | 
			
		||||
@@ -73,18 +79,18 @@ def _get_connection_settings(
 | 
			
		||||
    .. versionchanged:: 0.10.6 - added mongomock support
 | 
			
		||||
    """
 | 
			
		||||
    conn_settings = {
 | 
			
		||||
        'name': name or db or DEFAULT_DATABASE_NAME,
 | 
			
		||||
        'host': host or DEFAULT_HOST,
 | 
			
		||||
        'port': port or DEFAULT_PORT,
 | 
			
		||||
        'read_preference': read_preference,
 | 
			
		||||
        'username': username,
 | 
			
		||||
        'password': password,
 | 
			
		||||
        'authentication_source': authentication_source,
 | 
			
		||||
        'authentication_mechanism': authentication_mechanism
 | 
			
		||||
        "name": name or db or DEFAULT_DATABASE_NAME,
 | 
			
		||||
        "host": host or DEFAULT_HOST,
 | 
			
		||||
        "port": port or DEFAULT_PORT,
 | 
			
		||||
        "read_preference": read_preference,
 | 
			
		||||
        "username": username,
 | 
			
		||||
        "password": password,
 | 
			
		||||
        "authentication_source": authentication_source,
 | 
			
		||||
        "authentication_mechanism": authentication_mechanism,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _check_db_name(conn_settings['name'])
 | 
			
		||||
    conn_host = conn_settings['host']
 | 
			
		||||
    _check_db_name(conn_settings["name"])
 | 
			
		||||
    conn_host = conn_settings["host"]
 | 
			
		||||
 | 
			
		||||
    # Host can be a list or a string, so if string, force to a list.
 | 
			
		||||
    if isinstance(conn_host, six.string_types):
 | 
			
		||||
@@ -94,32 +100,32 @@ def _get_connection_settings(
 | 
			
		||||
    for entity in conn_host:
 | 
			
		||||
 | 
			
		||||
        # Handle Mongomock
 | 
			
		||||
        if entity.startswith('mongomock://'):
 | 
			
		||||
            conn_settings['is_mock'] = True
 | 
			
		||||
        if entity.startswith("mongomock://"):
 | 
			
		||||
            conn_settings["is_mock"] = True
 | 
			
		||||
            # `mongomock://` is not a valid url prefix and must be replaced by `mongodb://`
 | 
			
		||||
            resolved_hosts.append(entity.replace('mongomock://', 'mongodb://', 1))
 | 
			
		||||
            resolved_hosts.append(entity.replace("mongomock://", "mongodb://", 1))
 | 
			
		||||
 | 
			
		||||
        # Handle URI style connections, only updating connection params which
 | 
			
		||||
        # were explicitly specified in the URI.
 | 
			
		||||
        elif '://' in entity:
 | 
			
		||||
        elif "://" in entity:
 | 
			
		||||
            uri_dict = uri_parser.parse_uri(entity)
 | 
			
		||||
            resolved_hosts.append(entity)
 | 
			
		||||
 | 
			
		||||
            if uri_dict.get('database'):
 | 
			
		||||
                conn_settings['name'] = uri_dict.get('database')
 | 
			
		||||
            if uri_dict.get("database"):
 | 
			
		||||
                conn_settings["name"] = uri_dict.get("database")
 | 
			
		||||
 | 
			
		||||
            for param in ('read_preference', 'username', 'password'):
 | 
			
		||||
            for param in ("read_preference", "username", "password"):
 | 
			
		||||
                if uri_dict.get(param):
 | 
			
		||||
                    conn_settings[param] = uri_dict[param]
 | 
			
		||||
 | 
			
		||||
            uri_options = uri_dict['options']
 | 
			
		||||
            if 'replicaset' in uri_options:
 | 
			
		||||
                conn_settings['replicaSet'] = uri_options['replicaset']
 | 
			
		||||
            if 'authsource' in uri_options:
 | 
			
		||||
                conn_settings['authentication_source'] = uri_options['authsource']
 | 
			
		||||
            if 'authmechanism' in uri_options:
 | 
			
		||||
                conn_settings['authentication_mechanism'] = uri_options['authmechanism']
 | 
			
		||||
            if 'readpreference' in uri_options:
 | 
			
		||||
            uri_options = uri_dict["options"]
 | 
			
		||||
            if "replicaset" in uri_options:
 | 
			
		||||
                conn_settings["replicaSet"] = uri_options["replicaset"]
 | 
			
		||||
            if "authsource" in uri_options:
 | 
			
		||||
                conn_settings["authentication_source"] = uri_options["authsource"]
 | 
			
		||||
            if "authmechanism" in uri_options:
 | 
			
		||||
                conn_settings["authentication_mechanism"] = uri_options["authmechanism"]
 | 
			
		||||
            if "readpreference" in uri_options:
 | 
			
		||||
                read_preferences = (
 | 
			
		||||
                    ReadPreference.NEAREST,
 | 
			
		||||
                    ReadPreference.PRIMARY,
 | 
			
		||||
@@ -133,34 +139,41 @@ def _get_connection_settings(
 | 
			
		||||
                # int (e.g. 3).
 | 
			
		||||
                # TODO simplify the code below once we drop support for
 | 
			
		||||
                # PyMongo v3.4.
 | 
			
		||||
                read_pf_mode = uri_options['readpreference']
 | 
			
		||||
                read_pf_mode = uri_options["readpreference"]
 | 
			
		||||
                if isinstance(read_pf_mode, six.string_types):
 | 
			
		||||
                    read_pf_mode = read_pf_mode.lower()
 | 
			
		||||
                for preference in read_preferences:
 | 
			
		||||
                    if (
 | 
			
		||||
                        preference.name.lower() == read_pf_mode or
 | 
			
		||||
                        preference.mode == read_pf_mode
 | 
			
		||||
                        preference.name.lower() == read_pf_mode
 | 
			
		||||
                        or preference.mode == read_pf_mode
 | 
			
		||||
                    ):
 | 
			
		||||
                        conn_settings['read_preference'] = preference
 | 
			
		||||
                        conn_settings["read_preference"] = preference
 | 
			
		||||
                        break
 | 
			
		||||
        else:
 | 
			
		||||
            resolved_hosts.append(entity)
 | 
			
		||||
    conn_settings['host'] = resolved_hosts
 | 
			
		||||
    conn_settings["host"] = resolved_hosts
 | 
			
		||||
 | 
			
		||||
    # Deprecated parameters that should not be passed on
 | 
			
		||||
    kwargs.pop('slaves', None)
 | 
			
		||||
    kwargs.pop('is_slave', None)
 | 
			
		||||
    kwargs.pop("slaves", None)
 | 
			
		||||
    kwargs.pop("is_slave", None)
 | 
			
		||||
 | 
			
		||||
    conn_settings.update(kwargs)
 | 
			
		||||
    return conn_settings
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def register_connection(alias, db=None, name=None, host=None, port=None,
 | 
			
		||||
                        read_preference=READ_PREFERENCE,
 | 
			
		||||
                        username=None, password=None,
 | 
			
		||||
                        authentication_source=None,
 | 
			
		||||
                        authentication_mechanism=None,
 | 
			
		||||
                        **kwargs):
 | 
			
		||||
def register_connection(
 | 
			
		||||
    alias,
 | 
			
		||||
    db=None,
 | 
			
		||||
    name=None,
 | 
			
		||||
    host=None,
 | 
			
		||||
    port=None,
 | 
			
		||||
    read_preference=READ_PREFERENCE,
 | 
			
		||||
    username=None,
 | 
			
		||||
    password=None,
 | 
			
		||||
    authentication_source=None,
 | 
			
		||||
    authentication_mechanism=None,
 | 
			
		||||
    **kwargs
 | 
			
		||||
):
 | 
			
		||||
    """Register the connection settings.
 | 
			
		||||
 | 
			
		||||
    : param alias: the name that will be used to refer to this connection
 | 
			
		||||
@@ -185,12 +198,17 @@ def register_connection(alias, db=None, name=None, host=None, port=None,
 | 
			
		||||
    .. versionchanged:: 0.10.6 - added mongomock support
 | 
			
		||||
    """
 | 
			
		||||
    conn_settings = _get_connection_settings(
 | 
			
		||||
        db=db, name=name, host=host, port=port,
 | 
			
		||||
        db=db,
 | 
			
		||||
        name=name,
 | 
			
		||||
        host=host,
 | 
			
		||||
        port=port,
 | 
			
		||||
        read_preference=read_preference,
 | 
			
		||||
        username=username, password=password,
 | 
			
		||||
        username=username,
 | 
			
		||||
        password=password,
 | 
			
		||||
        authentication_source=authentication_source,
 | 
			
		||||
        authentication_mechanism=authentication_mechanism,
 | 
			
		||||
        **kwargs)
 | 
			
		||||
        **kwargs
 | 
			
		||||
    )
 | 
			
		||||
    _connection_settings[alias] = conn_settings
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -206,7 +224,7 @@ def disconnect(alias=DEFAULT_CONNECTION_NAME):
 | 
			
		||||
    if alias in _dbs:
 | 
			
		||||
        # Detach all cached collections in Documents
 | 
			
		||||
        for doc_cls in _get_documents_by_db(alias, DEFAULT_CONNECTION_NAME):
 | 
			
		||||
            if issubclass(doc_cls, Document):     # Skip EmbeddedDocument
 | 
			
		||||
            if issubclass(doc_cls, Document):  # Skip EmbeddedDocument
 | 
			
		||||
                doc_cls._disconnect()
 | 
			
		||||
 | 
			
		||||
        del _dbs[alias]
 | 
			
		||||
@@ -237,19 +255,21 @@ def get_connection(alias=DEFAULT_CONNECTION_NAME, reconnect=False):
 | 
			
		||||
    # Raise MongoEngineConnectionError if it doesn't.
 | 
			
		||||
    if alias not in _connection_settings:
 | 
			
		||||
        if alias == DEFAULT_CONNECTION_NAME:
 | 
			
		||||
            msg = 'You have not defined a default connection'
 | 
			
		||||
            msg = "You have not defined a default connection"
 | 
			
		||||
        else:
 | 
			
		||||
            msg = 'Connection with alias "%s" has not been defined' % alias
 | 
			
		||||
        raise MongoEngineConnectionError(msg)
 | 
			
		||||
 | 
			
		||||
    def _clean_settings(settings_dict):
 | 
			
		||||
        irrelevant_fields_set = {
 | 
			
		||||
            'name', 'username', 'password',
 | 
			
		||||
            'authentication_source', 'authentication_mechanism'
 | 
			
		||||
            "name",
 | 
			
		||||
            "username",
 | 
			
		||||
            "password",
 | 
			
		||||
            "authentication_source",
 | 
			
		||||
            "authentication_mechanism",
 | 
			
		||||
        }
 | 
			
		||||
        return {
 | 
			
		||||
            k: v for k, v in settings_dict.items()
 | 
			
		||||
            if k not in irrelevant_fields_set
 | 
			
		||||
            k: v for k, v in settings_dict.items() if k not in irrelevant_fields_set
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    raw_conn_settings = _connection_settings[alias].copy()
 | 
			
		||||
@@ -260,13 +280,12 @@ def get_connection(alias=DEFAULT_CONNECTION_NAME, reconnect=False):
 | 
			
		||||
    conn_settings = _clean_settings(raw_conn_settings)
 | 
			
		||||
 | 
			
		||||
    # Determine if we should use PyMongo's or mongomock's MongoClient.
 | 
			
		||||
    is_mock = conn_settings.pop('is_mock', False)
 | 
			
		||||
    is_mock = conn_settings.pop("is_mock", False)
 | 
			
		||||
    if is_mock:
 | 
			
		||||
        try:
 | 
			
		||||
            import mongomock
 | 
			
		||||
        except ImportError:
 | 
			
		||||
            raise RuntimeError('You need mongomock installed to mock '
 | 
			
		||||
                               'MongoEngine.')
 | 
			
		||||
            raise RuntimeError("You need mongomock installed to mock MongoEngine.")
 | 
			
		||||
        connection_class = mongomock.MongoClient
 | 
			
		||||
    else:
 | 
			
		||||
        connection_class = MongoClient
 | 
			
		||||
@@ -277,9 +296,7 @@ def get_connection(alias=DEFAULT_CONNECTION_NAME, reconnect=False):
 | 
			
		||||
        connection = existing_connection
 | 
			
		||||
    else:
 | 
			
		||||
        connection = _create_connection(
 | 
			
		||||
            alias=alias,
 | 
			
		||||
            connection_class=connection_class,
 | 
			
		||||
            **conn_settings
 | 
			
		||||
            alias=alias, connection_class=connection_class, **conn_settings
 | 
			
		||||
        )
 | 
			
		||||
    _connections[alias] = connection
 | 
			
		||||
    return _connections[alias]
 | 
			
		||||
@@ -294,7 +311,8 @@ def _create_connection(alias, connection_class, **connection_settings):
 | 
			
		||||
        return connection_class(**connection_settings)
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        raise MongoEngineConnectionError(
 | 
			
		||||
            'Cannot connect to database %s :\n%s' % (alias, e))
 | 
			
		||||
            "Cannot connect to database %s :\n%s" % (alias, e)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _find_existing_connection(connection_settings):
 | 
			
		||||
@@ -316,7 +334,7 @@ def _find_existing_connection(connection_settings):
 | 
			
		||||
        # Only remove the name but it's important to
 | 
			
		||||
        # keep the username/password/authentication_source/authentication_mechanism
 | 
			
		||||
        # to identify if the connection could be shared (cfr https://github.com/MongoEngine/mongoengine/issues/2047)
 | 
			
		||||
        return {k: v for k, v in settings_dict.items() if k != 'name'}
 | 
			
		||||
        return {k: v for k, v in settings_dict.items() if k != "name"}
 | 
			
		||||
 | 
			
		||||
    cleaned_conn_settings = _clean_settings(connection_settings)
 | 
			
		||||
    for db_alias, connection_settings in connection_settings_bis:
 | 
			
		||||
@@ -332,14 +350,18 @@ def get_db(alias=DEFAULT_CONNECTION_NAME, reconnect=False):
 | 
			
		||||
    if alias not in _dbs:
 | 
			
		||||
        conn = get_connection(alias)
 | 
			
		||||
        conn_settings = _connection_settings[alias]
 | 
			
		||||
        db = conn[conn_settings['name']]
 | 
			
		||||
        auth_kwargs = {'source': conn_settings['authentication_source']}
 | 
			
		||||
        if conn_settings['authentication_mechanism'] is not None:
 | 
			
		||||
            auth_kwargs['mechanism'] = conn_settings['authentication_mechanism']
 | 
			
		||||
        db = conn[conn_settings["name"]]
 | 
			
		||||
        auth_kwargs = {"source": conn_settings["authentication_source"]}
 | 
			
		||||
        if conn_settings["authentication_mechanism"] is not None:
 | 
			
		||||
            auth_kwargs["mechanism"] = conn_settings["authentication_mechanism"]
 | 
			
		||||
        # Authenticate if necessary
 | 
			
		||||
        if conn_settings['username'] and (conn_settings['password'] or
 | 
			
		||||
                                          conn_settings['authentication_mechanism'] == 'MONGODB-X509'):
 | 
			
		||||
            db.authenticate(conn_settings['username'], conn_settings['password'], **auth_kwargs)
 | 
			
		||||
        if conn_settings["username"] and (
 | 
			
		||||
            conn_settings["password"]
 | 
			
		||||
            or conn_settings["authentication_mechanism"] == "MONGODB-X509"
 | 
			
		||||
        ):
 | 
			
		||||
            db.authenticate(
 | 
			
		||||
                conn_settings["username"], conn_settings["password"], **auth_kwargs
 | 
			
		||||
            )
 | 
			
		||||
        _dbs[alias] = db
 | 
			
		||||
    return _dbs[alias]
 | 
			
		||||
 | 
			
		||||
@@ -368,8 +390,8 @@ def connect(db=None, alias=DEFAULT_CONNECTION_NAME, **kwargs):
 | 
			
		||||
 | 
			
		||||
        if new_conn_settings != prev_conn_setting:
 | 
			
		||||
            err_msg = (
 | 
			
		||||
                u'A different connection with alias `{}` was already '
 | 
			
		||||
                u'registered. Use disconnect() first'
 | 
			
		||||
                u"A different connection with alias `{}` was already "
 | 
			
		||||
                u"registered. Use disconnect() first"
 | 
			
		||||
            ).format(alias)
 | 
			
		||||
            raise MongoEngineConnectionError(err_msg)
 | 
			
		||||
    else:
 | 
			
		||||
 
 | 
			
		||||
@@ -7,8 +7,14 @@ from mongoengine.common import _import_class
 | 
			
		||||
from mongoengine.connection import DEFAULT_CONNECTION_NAME, get_db
 | 
			
		||||
from mongoengine.pymongo_support import count_documents
 | 
			
		||||
 | 
			
		||||
__all__ = ('switch_db', 'switch_collection', 'no_dereference',
 | 
			
		||||
           'no_sub_classes', 'query_counter', 'set_write_concern')
 | 
			
		||||
__all__ = (
 | 
			
		||||
    "switch_db",
 | 
			
		||||
    "switch_collection",
 | 
			
		||||
    "no_dereference",
 | 
			
		||||
    "no_sub_classes",
 | 
			
		||||
    "query_counter",
 | 
			
		||||
    "set_write_concern",
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class switch_db(object):
 | 
			
		||||
@@ -38,17 +44,17 @@ class switch_db(object):
 | 
			
		||||
        self.cls = cls
 | 
			
		||||
        self.collection = cls._get_collection()
 | 
			
		||||
        self.db_alias = db_alias
 | 
			
		||||
        self.ori_db_alias = cls._meta.get('db_alias', DEFAULT_CONNECTION_NAME)
 | 
			
		||||
        self.ori_db_alias = cls._meta.get("db_alias", DEFAULT_CONNECTION_NAME)
 | 
			
		||||
 | 
			
		||||
    def __enter__(self):
 | 
			
		||||
        """Change the db_alias and clear the cached collection."""
 | 
			
		||||
        self.cls._meta['db_alias'] = self.db_alias
 | 
			
		||||
        self.cls._meta["db_alias"] = self.db_alias
 | 
			
		||||
        self.cls._collection = None
 | 
			
		||||
        return self.cls
 | 
			
		||||
 | 
			
		||||
    def __exit__(self, t, value, traceback):
 | 
			
		||||
        """Reset the db_alias and collection."""
 | 
			
		||||
        self.cls._meta['db_alias'] = self.ori_db_alias
 | 
			
		||||
        self.cls._meta["db_alias"] = self.ori_db_alias
 | 
			
		||||
        self.cls._collection = self.collection
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -111,14 +117,15 @@ class no_dereference(object):
 | 
			
		||||
        """
 | 
			
		||||
        self.cls = cls
 | 
			
		||||
 | 
			
		||||
        ReferenceField = _import_class('ReferenceField')
 | 
			
		||||
        GenericReferenceField = _import_class('GenericReferenceField')
 | 
			
		||||
        ComplexBaseField = _import_class('ComplexBaseField')
 | 
			
		||||
        ReferenceField = _import_class("ReferenceField")
 | 
			
		||||
        GenericReferenceField = _import_class("GenericReferenceField")
 | 
			
		||||
        ComplexBaseField = _import_class("ComplexBaseField")
 | 
			
		||||
 | 
			
		||||
        self.deref_fields = [k for k, v in iteritems(self.cls._fields)
 | 
			
		||||
                             if isinstance(v, (ReferenceField,
 | 
			
		||||
                                               GenericReferenceField,
 | 
			
		||||
                                               ComplexBaseField))]
 | 
			
		||||
        self.deref_fields = [
 | 
			
		||||
            k
 | 
			
		||||
            for k, v in iteritems(self.cls._fields)
 | 
			
		||||
            if isinstance(v, (ReferenceField, GenericReferenceField, ComplexBaseField))
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
    def __enter__(self):
 | 
			
		||||
        """Change the objects default and _auto_dereference values."""
 | 
			
		||||
@@ -180,15 +187,12 @@ class query_counter(object):
 | 
			
		||||
        """
 | 
			
		||||
        self.db = get_db()
 | 
			
		||||
        self.initial_profiling_level = None
 | 
			
		||||
        self._ctx_query_counter = 0             # number of queries issued by the context
 | 
			
		||||
        self._ctx_query_counter = 0  # number of queries issued by the context
 | 
			
		||||
 | 
			
		||||
        self._ignored_query = {
 | 
			
		||||
            'ns':
 | 
			
		||||
                {'$ne': '%s.system.indexes' % self.db.name},
 | 
			
		||||
            'op':                       # MONGODB < 3.2
 | 
			
		||||
                {'$ne': 'killcursors'},
 | 
			
		||||
            'command.killCursors':      # MONGODB >= 3.2
 | 
			
		||||
                {'$exists': False}
 | 
			
		||||
            "ns": {"$ne": "%s.system.indexes" % self.db.name},
 | 
			
		||||
            "op": {"$ne": "killcursors"},  # MONGODB < 3.2
 | 
			
		||||
            "command.killCursors": {"$exists": False},  # MONGODB >= 3.2
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    def _turn_on_profiling(self):
 | 
			
		||||
@@ -238,8 +242,13 @@ class query_counter(object):
 | 
			
		||||
        and substracting the queries issued by this context. In fact everytime this is called, 1 query is
 | 
			
		||||
        issued so we need to balance that
 | 
			
		||||
        """
 | 
			
		||||
        count = count_documents(self.db.system.profile, self._ignored_query) - self._ctx_query_counter
 | 
			
		||||
        self._ctx_query_counter += 1    # Account for the query we just issued to gather the information
 | 
			
		||||
        count = (
 | 
			
		||||
            count_documents(self.db.system.profile, self._ignored_query)
 | 
			
		||||
            - self._ctx_query_counter
 | 
			
		||||
        )
 | 
			
		||||
        self._ctx_query_counter += (
 | 
			
		||||
            1
 | 
			
		||||
        )  # Account for the query we just issued to gather the information
 | 
			
		||||
        return count
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,8 +2,13 @@ from bson import DBRef, SON
 | 
			
		||||
import six
 | 
			
		||||
from six import iteritems
 | 
			
		||||
 | 
			
		||||
from mongoengine.base import (BaseDict, BaseList, EmbeddedDocumentList,
 | 
			
		||||
                              TopLevelDocumentMetaclass, get_document)
 | 
			
		||||
from mongoengine.base import (
 | 
			
		||||
    BaseDict,
 | 
			
		||||
    BaseList,
 | 
			
		||||
    EmbeddedDocumentList,
 | 
			
		||||
    TopLevelDocumentMetaclass,
 | 
			
		||||
    get_document,
 | 
			
		||||
)
 | 
			
		||||
from mongoengine.base.datastructures import LazyReference
 | 
			
		||||
from mongoengine.connection import get_db
 | 
			
		||||
from mongoengine.document import Document, EmbeddedDocument
 | 
			
		||||
@@ -36,21 +41,23 @@ class DeReference(object):
 | 
			
		||||
        self.max_depth = max_depth
 | 
			
		||||
        doc_type = None
 | 
			
		||||
 | 
			
		||||
        if instance and isinstance(instance, (Document, EmbeddedDocument,
 | 
			
		||||
                                              TopLevelDocumentMetaclass)):
 | 
			
		||||
        if instance and isinstance(
 | 
			
		||||
            instance, (Document, EmbeddedDocument, TopLevelDocumentMetaclass)
 | 
			
		||||
        ):
 | 
			
		||||
            doc_type = instance._fields.get(name)
 | 
			
		||||
            while hasattr(doc_type, 'field'):
 | 
			
		||||
            while hasattr(doc_type, "field"):
 | 
			
		||||
                doc_type = doc_type.field
 | 
			
		||||
 | 
			
		||||
            if isinstance(doc_type, ReferenceField):
 | 
			
		||||
                field = doc_type
 | 
			
		||||
                doc_type = doc_type.document_type
 | 
			
		||||
                is_list = not hasattr(items, 'items')
 | 
			
		||||
                is_list = not hasattr(items, "items")
 | 
			
		||||
 | 
			
		||||
                if is_list and all([i.__class__ == doc_type for i in items]):
 | 
			
		||||
                    return items
 | 
			
		||||
                elif not is_list and all(
 | 
			
		||||
                        [i.__class__ == doc_type for i in items.values()]):
 | 
			
		||||
                    [i.__class__ == doc_type for i in items.values()]
 | 
			
		||||
                ):
 | 
			
		||||
                    return items
 | 
			
		||||
                elif not field.dbref:
 | 
			
		||||
                    # We must turn the ObjectIds into DBRefs
 | 
			
		||||
@@ -83,7 +90,7 @@ class DeReference(object):
 | 
			
		||||
                            new_items[k] = value
 | 
			
		||||
                        return new_items
 | 
			
		||||
 | 
			
		||||
                    if not hasattr(items, 'items'):
 | 
			
		||||
                    if not hasattr(items, "items"):
 | 
			
		||||
                        items = _get_items_from_list(items)
 | 
			
		||||
                    else:
 | 
			
		||||
                        items = _get_items_from_dict(items)
 | 
			
		||||
@@ -120,13 +127,19 @@ class DeReference(object):
 | 
			
		||||
                        continue
 | 
			
		||||
                    elif isinstance(v, DBRef):
 | 
			
		||||
                        reference_map.setdefault(field.document_type, set()).add(v.id)
 | 
			
		||||
                    elif isinstance(v, (dict, SON)) and '_ref' in v:
 | 
			
		||||
                        reference_map.setdefault(get_document(v['_cls']), set()).add(v['_ref'].id)
 | 
			
		||||
                    elif isinstance(v, (dict, SON)) and "_ref" in v:
 | 
			
		||||
                        reference_map.setdefault(get_document(v["_cls"]), set()).add(
 | 
			
		||||
                            v["_ref"].id
 | 
			
		||||
                        )
 | 
			
		||||
                    elif isinstance(v, (dict, list, tuple)) and depth <= self.max_depth:
 | 
			
		||||
                        field_cls = getattr(getattr(field, 'field', None), 'document_type', None)
 | 
			
		||||
                        field_cls = getattr(
 | 
			
		||||
                            getattr(field, "field", None), "document_type", None
 | 
			
		||||
                        )
 | 
			
		||||
                        references = self._find_references(v, depth)
 | 
			
		||||
                        for key, refs in iteritems(references):
 | 
			
		||||
                            if isinstance(field_cls, (Document, TopLevelDocumentMetaclass)):
 | 
			
		||||
                            if isinstance(
 | 
			
		||||
                                field_cls, (Document, TopLevelDocumentMetaclass)
 | 
			
		||||
                            ):
 | 
			
		||||
                                key = field_cls
 | 
			
		||||
                            reference_map.setdefault(key, set()).update(refs)
 | 
			
		||||
            elif isinstance(item, LazyReference):
 | 
			
		||||
@@ -134,8 +147,10 @@ class DeReference(object):
 | 
			
		||||
                continue
 | 
			
		||||
            elif isinstance(item, DBRef):
 | 
			
		||||
                reference_map.setdefault(item.collection, set()).add(item.id)
 | 
			
		||||
            elif isinstance(item, (dict, SON)) and '_ref' in item:
 | 
			
		||||
                reference_map.setdefault(get_document(item['_cls']), set()).add(item['_ref'].id)
 | 
			
		||||
            elif isinstance(item, (dict, SON)) and "_ref" in item:
 | 
			
		||||
                reference_map.setdefault(get_document(item["_cls"]), set()).add(
 | 
			
		||||
                    item["_ref"].id
 | 
			
		||||
                )
 | 
			
		||||
            elif isinstance(item, (dict, list, tuple)) and depth - 1 <= self.max_depth:
 | 
			
		||||
                references = self._find_references(item, depth - 1)
 | 
			
		||||
                for key, refs in iteritems(references):
 | 
			
		||||
@@ -151,12 +166,13 @@ class DeReference(object):
 | 
			
		||||
 | 
			
		||||
            # we use getattr instead of hasattr because hasattr swallows any exception under python2
 | 
			
		||||
            # so it could hide nasty things without raising exceptions (cfr bug #1688))
 | 
			
		||||
            ref_document_cls_exists = (getattr(collection, 'objects', None) is not None)
 | 
			
		||||
            ref_document_cls_exists = getattr(collection, "objects", None) is not None
 | 
			
		||||
 | 
			
		||||
            if ref_document_cls_exists:
 | 
			
		||||
                col_name = collection._get_collection_name()
 | 
			
		||||
                refs = [dbref for dbref in dbrefs
 | 
			
		||||
                        if (col_name, dbref) not in object_map]
 | 
			
		||||
                refs = [
 | 
			
		||||
                    dbref for dbref in dbrefs if (col_name, dbref) not in object_map
 | 
			
		||||
                ]
 | 
			
		||||
                references = collection.objects.in_bulk(refs)
 | 
			
		||||
                for key, doc in iteritems(references):
 | 
			
		||||
                    object_map[(col_name, key)] = doc
 | 
			
		||||
@@ -164,23 +180,26 @@ class DeReference(object):
 | 
			
		||||
                if isinstance(doc_type, (ListField, DictField, MapField)):
 | 
			
		||||
                    continue
 | 
			
		||||
 | 
			
		||||
                refs = [dbref for dbref in dbrefs
 | 
			
		||||
                        if (collection, dbref) not in object_map]
 | 
			
		||||
                refs = [
 | 
			
		||||
                    dbref for dbref in dbrefs if (collection, dbref) not in object_map
 | 
			
		||||
                ]
 | 
			
		||||
 | 
			
		||||
                if doc_type:
 | 
			
		||||
                    references = doc_type._get_db()[collection].find({'_id': {'$in': refs}})
 | 
			
		||||
                    references = doc_type._get_db()[collection].find(
 | 
			
		||||
                        {"_id": {"$in": refs}}
 | 
			
		||||
                    )
 | 
			
		||||
                    for ref in references:
 | 
			
		||||
                        doc = doc_type._from_son(ref)
 | 
			
		||||
                        object_map[(collection, doc.id)] = doc
 | 
			
		||||
                else:
 | 
			
		||||
                    references = get_db()[collection].find({'_id': {'$in': refs}})
 | 
			
		||||
                    references = get_db()[collection].find({"_id": {"$in": refs}})
 | 
			
		||||
                    for ref in references:
 | 
			
		||||
                        if '_cls' in ref:
 | 
			
		||||
                            doc = get_document(ref['_cls'])._from_son(ref)
 | 
			
		||||
                        if "_cls" in ref:
 | 
			
		||||
                            doc = get_document(ref["_cls"])._from_son(ref)
 | 
			
		||||
                        elif doc_type is None:
 | 
			
		||||
                            doc = get_document(
 | 
			
		||||
                                ''.join(x.capitalize()
 | 
			
		||||
                                        for x in collection.split('_')))._from_son(ref)
 | 
			
		||||
                                "".join(x.capitalize() for x in collection.split("_"))
 | 
			
		||||
                            )._from_son(ref)
 | 
			
		||||
                        else:
 | 
			
		||||
                            doc = doc_type._from_son(ref)
 | 
			
		||||
                        object_map[(collection, doc.id)] = doc
 | 
			
		||||
@@ -208,19 +227,20 @@ class DeReference(object):
 | 
			
		||||
                    return BaseList(items, instance, name)
 | 
			
		||||
 | 
			
		||||
        if isinstance(items, (dict, SON)):
 | 
			
		||||
            if '_ref' in items:
 | 
			
		||||
            if "_ref" in items:
 | 
			
		||||
                return self.object_map.get(
 | 
			
		||||
                    (items['_ref'].collection, items['_ref'].id), items)
 | 
			
		||||
            elif '_cls' in items:
 | 
			
		||||
                doc = get_document(items['_cls'])._from_son(items)
 | 
			
		||||
                _cls = doc._data.pop('_cls', None)
 | 
			
		||||
                del items['_cls']
 | 
			
		||||
                    (items["_ref"].collection, items["_ref"].id), items
 | 
			
		||||
                )
 | 
			
		||||
            elif "_cls" in items:
 | 
			
		||||
                doc = get_document(items["_cls"])._from_son(items)
 | 
			
		||||
                _cls = doc._data.pop("_cls", None)
 | 
			
		||||
                del items["_cls"]
 | 
			
		||||
                doc._data = self._attach_objects(doc._data, depth, doc, None)
 | 
			
		||||
                if _cls is not None:
 | 
			
		||||
                    doc._data['_cls'] = _cls
 | 
			
		||||
                    doc._data["_cls"] = _cls
 | 
			
		||||
                return doc
 | 
			
		||||
 | 
			
		||||
        if not hasattr(items, 'items'):
 | 
			
		||||
        if not hasattr(items, "items"):
 | 
			
		||||
            is_list = True
 | 
			
		||||
            list_type = BaseList
 | 
			
		||||
            if isinstance(items, EmbeddedDocumentList):
 | 
			
		||||
@@ -247,17 +267,25 @@ class DeReference(object):
 | 
			
		||||
                    v = data[k]._data.get(field_name, None)
 | 
			
		||||
                    if isinstance(v, DBRef):
 | 
			
		||||
                        data[k]._data[field_name] = self.object_map.get(
 | 
			
		||||
                            (v.collection, v.id), v)
 | 
			
		||||
                    elif isinstance(v, (dict, SON)) and '_ref' in v:
 | 
			
		||||
                            (v.collection, v.id), v
 | 
			
		||||
                        )
 | 
			
		||||
                    elif isinstance(v, (dict, SON)) and "_ref" in v:
 | 
			
		||||
                        data[k]._data[field_name] = self.object_map.get(
 | 
			
		||||
                            (v['_ref'].collection, v['_ref'].id), v)
 | 
			
		||||
                            (v["_ref"].collection, v["_ref"].id), v
 | 
			
		||||
                        )
 | 
			
		||||
                    elif isinstance(v, (dict, list, tuple)) and depth <= self.max_depth:
 | 
			
		||||
                        item_name = six.text_type('{0}.{1}.{2}').format(name, k, field_name)
 | 
			
		||||
                        data[k]._data[field_name] = self._attach_objects(v, depth, instance=instance, name=item_name)
 | 
			
		||||
                        item_name = six.text_type("{0}.{1}.{2}").format(
 | 
			
		||||
                            name, k, field_name
 | 
			
		||||
                        )
 | 
			
		||||
                        data[k]._data[field_name] = self._attach_objects(
 | 
			
		||||
                            v, depth, instance=instance, name=item_name
 | 
			
		||||
                        )
 | 
			
		||||
            elif isinstance(v, (dict, list, tuple)) and depth <= self.max_depth:
 | 
			
		||||
                item_name = '%s.%s' % (name, k) if name else name
 | 
			
		||||
                data[k] = self._attach_objects(v, depth - 1, instance=instance, name=item_name)
 | 
			
		||||
            elif isinstance(v, DBRef) and hasattr(v, 'id'):
 | 
			
		||||
                item_name = "%s.%s" % (name, k) if name else name
 | 
			
		||||
                data[k] = self._attach_objects(
 | 
			
		||||
                    v, depth - 1, instance=instance, name=item_name
 | 
			
		||||
                )
 | 
			
		||||
            elif isinstance(v, DBRef) and hasattr(v, "id"):
 | 
			
		||||
                data[k] = self.object_map.get((v.collection, v.id), v)
 | 
			
		||||
 | 
			
		||||
        if instance and name:
 | 
			
		||||
 
 | 
			
		||||
@@ -8,23 +8,36 @@ import six
 | 
			
		||||
from six import iteritems
 | 
			
		||||
 | 
			
		||||
from mongoengine import signals
 | 
			
		||||
from mongoengine.base import (BaseDict, BaseDocument, BaseList,
 | 
			
		||||
                              DocumentMetaclass, EmbeddedDocumentList,
 | 
			
		||||
                              TopLevelDocumentMetaclass, get_document)
 | 
			
		||||
from mongoengine.base import (
 | 
			
		||||
    BaseDict,
 | 
			
		||||
    BaseDocument,
 | 
			
		||||
    BaseList,
 | 
			
		||||
    DocumentMetaclass,
 | 
			
		||||
    EmbeddedDocumentList,
 | 
			
		||||
    TopLevelDocumentMetaclass,
 | 
			
		||||
    get_document,
 | 
			
		||||
)
 | 
			
		||||
from mongoengine.common import _import_class
 | 
			
		||||
from mongoengine.connection import DEFAULT_CONNECTION_NAME, get_db
 | 
			
		||||
from mongoengine.context_managers import (set_write_concern,
 | 
			
		||||
                                          switch_collection,
 | 
			
		||||
                                          switch_db)
 | 
			
		||||
from mongoengine.errors import (InvalidDocumentError, InvalidQueryError,
 | 
			
		||||
                                SaveConditionError)
 | 
			
		||||
from mongoengine.context_managers import set_write_concern, switch_collection, switch_db
 | 
			
		||||
from mongoengine.errors import (
 | 
			
		||||
    InvalidDocumentError,
 | 
			
		||||
    InvalidQueryError,
 | 
			
		||||
    SaveConditionError,
 | 
			
		||||
)
 | 
			
		||||
from mongoengine.pymongo_support import list_collection_names
 | 
			
		||||
from mongoengine.queryset import (NotUniqueError, OperationError,
 | 
			
		||||
                                  QuerySet, transform)
 | 
			
		||||
from mongoengine.queryset import NotUniqueError, OperationError, QuerySet, transform
 | 
			
		||||
 | 
			
		||||
__all__ = ('Document', 'EmbeddedDocument', 'DynamicDocument',
 | 
			
		||||
           'DynamicEmbeddedDocument', 'OperationError',
 | 
			
		||||
           'InvalidCollectionError', 'NotUniqueError', 'MapReduceDocument')
 | 
			
		||||
__all__ = (
 | 
			
		||||
    "Document",
 | 
			
		||||
    "EmbeddedDocument",
 | 
			
		||||
    "DynamicDocument",
 | 
			
		||||
    "DynamicEmbeddedDocument",
 | 
			
		||||
    "OperationError",
 | 
			
		||||
    "InvalidCollectionError",
 | 
			
		||||
    "NotUniqueError",
 | 
			
		||||
    "MapReduceDocument",
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def includes_cls(fields):
 | 
			
		||||
@@ -35,7 +48,7 @@ def includes_cls(fields):
 | 
			
		||||
            first_field = fields[0]
 | 
			
		||||
        elif isinstance(fields[0], (list, tuple)) and len(fields[0]):
 | 
			
		||||
            first_field = fields[0][0]
 | 
			
		||||
    return first_field == '_cls'
 | 
			
		||||
    return first_field == "_cls"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class InvalidCollectionError(Exception):
 | 
			
		||||
@@ -56,7 +69,7 @@ class EmbeddedDocument(six.with_metaclass(DocumentMetaclass, BaseDocument)):
 | 
			
		||||
    :attr:`meta` dictionary.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    __slots__ = ('_instance', )
 | 
			
		||||
    __slots__ = ("_instance",)
 | 
			
		||||
 | 
			
		||||
    # The __metaclass__ attribute is removed by 2to3 when running with Python3
 | 
			
		||||
    # my_metaclass is defined so that metaclass can be queried in Python 2 & 3
 | 
			
		||||
@@ -85,8 +98,8 @@ class EmbeddedDocument(six.with_metaclass(DocumentMetaclass, BaseDocument)):
 | 
			
		||||
        data = super(EmbeddedDocument, self).to_mongo(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
        # remove _id from the SON if it's in it and it's None
 | 
			
		||||
        if '_id' in data and data['_id'] is None:
 | 
			
		||||
            del data['_id']
 | 
			
		||||
        if "_id" in data and data["_id"] is None:
 | 
			
		||||
            del data["_id"]
 | 
			
		||||
 | 
			
		||||
        return data
 | 
			
		||||
 | 
			
		||||
@@ -147,19 +160,19 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
 | 
			
		||||
    # my_metaclass is defined so that metaclass can be queried in Python 2 & 3
 | 
			
		||||
    my_metaclass = TopLevelDocumentMetaclass
 | 
			
		||||
 | 
			
		||||
    __slots__ = ('__objects',)
 | 
			
		||||
    __slots__ = ("__objects",)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def pk(self):
 | 
			
		||||
        """Get the primary key."""
 | 
			
		||||
        if 'id_field' not in self._meta:
 | 
			
		||||
        if "id_field" not in self._meta:
 | 
			
		||||
            return None
 | 
			
		||||
        return getattr(self, self._meta['id_field'])
 | 
			
		||||
        return getattr(self, self._meta["id_field"])
 | 
			
		||||
 | 
			
		||||
    @pk.setter
 | 
			
		||||
    def pk(self, value):
 | 
			
		||||
        """Set the primary key."""
 | 
			
		||||
        return setattr(self, self._meta['id_field'], value)
 | 
			
		||||
        return setattr(self, self._meta["id_field"], value)
 | 
			
		||||
 | 
			
		||||
    def __hash__(self):
 | 
			
		||||
        """Return the hash based on the PK of this document. If it's new
 | 
			
		||||
@@ -173,7 +186,7 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def _get_db(cls):
 | 
			
		||||
        """Some Model using other db_alias"""
 | 
			
		||||
        return get_db(cls._meta.get('db_alias', DEFAULT_CONNECTION_NAME))
 | 
			
		||||
        return get_db(cls._meta.get("db_alias", DEFAULT_CONNECTION_NAME))
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def _disconnect(cls):
 | 
			
		||||
@@ -190,9 +203,9 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
 | 
			
		||||
        2. Creates indexes defined in this document's :attr:`meta` dictionary.
 | 
			
		||||
           This happens only if `auto_create_index` is True.
 | 
			
		||||
        """
 | 
			
		||||
        if not hasattr(cls, '_collection') or cls._collection is None:
 | 
			
		||||
        if not hasattr(cls, "_collection") or cls._collection is None:
 | 
			
		||||
            # Get the collection, either capped or regular.
 | 
			
		||||
            if cls._meta.get('max_size') or cls._meta.get('max_documents'):
 | 
			
		||||
            if cls._meta.get("max_size") or cls._meta.get("max_documents"):
 | 
			
		||||
                cls._collection = cls._get_capped_collection()
 | 
			
		||||
            else:
 | 
			
		||||
                db = cls._get_db()
 | 
			
		||||
@@ -203,8 +216,7 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
 | 
			
		||||
            # set to False.
 | 
			
		||||
            # Also there is no need to ensure indexes on slave.
 | 
			
		||||
            db = cls._get_db()
 | 
			
		||||
            if cls._meta.get('auto_create_index', True) and\
 | 
			
		||||
                    db.client.is_primary:
 | 
			
		||||
            if cls._meta.get("auto_create_index", True) and db.client.is_primary:
 | 
			
		||||
                cls.ensure_indexes()
 | 
			
		||||
 | 
			
		||||
        return cls._collection
 | 
			
		||||
@@ -216,8 +228,8 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
 | 
			
		||||
        collection_name = cls._get_collection_name()
 | 
			
		||||
 | 
			
		||||
        # Get max document limit and max byte size from meta.
 | 
			
		||||
        max_size = cls._meta.get('max_size') or 10 * 2 ** 20  # 10MB default
 | 
			
		||||
        max_documents = cls._meta.get('max_documents')
 | 
			
		||||
        max_size = cls._meta.get("max_size") or 10 * 2 ** 20  # 10MB default
 | 
			
		||||
        max_documents = cls._meta.get("max_documents")
 | 
			
		||||
 | 
			
		||||
        # MongoDB will automatically raise the size to make it a multiple of
 | 
			
		||||
        # 256 bytes. We raise it here ourselves to be able to reliably compare
 | 
			
		||||
@@ -227,24 +239,23 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
 | 
			
		||||
 | 
			
		||||
        # If the collection already exists and has different options
 | 
			
		||||
        # (i.e. isn't capped or has different max/size), raise an error.
 | 
			
		||||
        if collection_name in list_collection_names(db, include_system_collections=True):
 | 
			
		||||
        if collection_name in list_collection_names(
 | 
			
		||||
            db, include_system_collections=True
 | 
			
		||||
        ):
 | 
			
		||||
            collection = db[collection_name]
 | 
			
		||||
            options = collection.options()
 | 
			
		||||
            if (
 | 
			
		||||
                options.get('max') != max_documents or
 | 
			
		||||
                options.get('size') != max_size
 | 
			
		||||
            ):
 | 
			
		||||
            if options.get("max") != max_documents or options.get("size") != max_size:
 | 
			
		||||
                raise InvalidCollectionError(
 | 
			
		||||
                    'Cannot create collection "{}" as a capped '
 | 
			
		||||
                    'collection as it already exists'.format(cls._collection)
 | 
			
		||||
                    "collection as it already exists".format(cls._collection)
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
            return collection
 | 
			
		||||
 | 
			
		||||
        # Create a new capped collection.
 | 
			
		||||
        opts = {'capped': True, 'size': max_size}
 | 
			
		||||
        opts = {"capped": True, "size": max_size}
 | 
			
		||||
        if max_documents:
 | 
			
		||||
            opts['max'] = max_documents
 | 
			
		||||
            opts["max"] = max_documents
 | 
			
		||||
 | 
			
		||||
        return db.create_collection(collection_name, **opts)
 | 
			
		||||
 | 
			
		||||
@@ -253,11 +264,11 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
 | 
			
		||||
 | 
			
		||||
        # If '_id' is None, try and set it from self._data. If that
 | 
			
		||||
        # doesn't exist either, remove '_id' from the SON completely.
 | 
			
		||||
        if data['_id'] is None:
 | 
			
		||||
            if self._data.get('id') is None:
 | 
			
		||||
                del data['_id']
 | 
			
		||||
        if data["_id"] is None:
 | 
			
		||||
            if self._data.get("id") is None:
 | 
			
		||||
                del data["_id"]
 | 
			
		||||
            else:
 | 
			
		||||
                data['_id'] = self._data['id']
 | 
			
		||||
                data["_id"] = self._data["id"]
 | 
			
		||||
 | 
			
		||||
        return data
 | 
			
		||||
 | 
			
		||||
@@ -279,15 +290,17 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
 | 
			
		||||
            query = {}
 | 
			
		||||
 | 
			
		||||
        if self.pk is None:
 | 
			
		||||
            raise InvalidDocumentError('The document does not have a primary key.')
 | 
			
		||||
            raise InvalidDocumentError("The document does not have a primary key.")
 | 
			
		||||
 | 
			
		||||
        id_field = self._meta['id_field']
 | 
			
		||||
        id_field = self._meta["id_field"]
 | 
			
		||||
        query = query.copy() if isinstance(query, dict) else query.to_query(self)
 | 
			
		||||
 | 
			
		||||
        if id_field not in query:
 | 
			
		||||
            query[id_field] = self.pk
 | 
			
		||||
        elif query[id_field] != self.pk:
 | 
			
		||||
            raise InvalidQueryError('Invalid document modify query: it must modify only this document.')
 | 
			
		||||
            raise InvalidQueryError(
 | 
			
		||||
                "Invalid document modify query: it must modify only this document."
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        # Need to add shard key to query, or you get an error
 | 
			
		||||
        query.update(self._object_key)
 | 
			
		||||
@@ -304,9 +317,19 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
 | 
			
		||||
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def save(self, force_insert=False, validate=True, clean=True,
 | 
			
		||||
             write_concern=None, cascade=None, cascade_kwargs=None,
 | 
			
		||||
             _refs=None, save_condition=None, signal_kwargs=None, **kwargs):
 | 
			
		||||
    def save(
 | 
			
		||||
        self,
 | 
			
		||||
        force_insert=False,
 | 
			
		||||
        validate=True,
 | 
			
		||||
        clean=True,
 | 
			
		||||
        write_concern=None,
 | 
			
		||||
        cascade=None,
 | 
			
		||||
        cascade_kwargs=None,
 | 
			
		||||
        _refs=None,
 | 
			
		||||
        save_condition=None,
 | 
			
		||||
        signal_kwargs=None,
 | 
			
		||||
        **kwargs
 | 
			
		||||
    ):
 | 
			
		||||
        """Save the :class:`~mongoengine.Document` to the database. If the
 | 
			
		||||
        document already exists, it will be updated, otherwise it will be
 | 
			
		||||
        created.
 | 
			
		||||
@@ -360,8 +383,8 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
 | 
			
		||||
        """
 | 
			
		||||
        signal_kwargs = signal_kwargs or {}
 | 
			
		||||
 | 
			
		||||
        if self._meta.get('abstract'):
 | 
			
		||||
            raise InvalidDocumentError('Cannot save an abstract document.')
 | 
			
		||||
        if self._meta.get("abstract"):
 | 
			
		||||
            raise InvalidDocumentError("Cannot save an abstract document.")
 | 
			
		||||
 | 
			
		||||
        signals.pre_save.send(self.__class__, document=self, **signal_kwargs)
 | 
			
		||||
 | 
			
		||||
@@ -371,15 +394,16 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
 | 
			
		||||
        if write_concern is None:
 | 
			
		||||
            write_concern = {}
 | 
			
		||||
 | 
			
		||||
        doc_id = self.to_mongo(fields=[self._meta['id_field']])
 | 
			
		||||
        created = ('_id' not in doc_id or self._created or force_insert)
 | 
			
		||||
        doc_id = self.to_mongo(fields=[self._meta["id_field"]])
 | 
			
		||||
        created = "_id" not in doc_id or self._created or force_insert
 | 
			
		||||
 | 
			
		||||
        signals.pre_save_post_validation.send(self.__class__, document=self,
 | 
			
		||||
                                              created=created, **signal_kwargs)
 | 
			
		||||
        signals.pre_save_post_validation.send(
 | 
			
		||||
            self.__class__, document=self, created=created, **signal_kwargs
 | 
			
		||||
        )
 | 
			
		||||
        # it might be refreshed by the pre_save_post_validation hook, e.g., for etag generation
 | 
			
		||||
        doc = self.to_mongo()
 | 
			
		||||
 | 
			
		||||
        if self._meta.get('auto_create_index', True):
 | 
			
		||||
        if self._meta.get("auto_create_index", True):
 | 
			
		||||
            self.ensure_indexes()
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
@@ -387,44 +411,45 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
 | 
			
		||||
            if created:
 | 
			
		||||
                object_id = self._save_create(doc, force_insert, write_concern)
 | 
			
		||||
            else:
 | 
			
		||||
                object_id, created = self._save_update(doc, save_condition,
 | 
			
		||||
                                                       write_concern)
 | 
			
		||||
                object_id, created = self._save_update(
 | 
			
		||||
                    doc, save_condition, write_concern
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
            if cascade is None:
 | 
			
		||||
                cascade = (self._meta.get('cascade', False) or
 | 
			
		||||
                           cascade_kwargs is not None)
 | 
			
		||||
                cascade = self._meta.get("cascade", False) or cascade_kwargs is not None
 | 
			
		||||
 | 
			
		||||
            if cascade:
 | 
			
		||||
                kwargs = {
 | 
			
		||||
                    'force_insert': force_insert,
 | 
			
		||||
                    'validate': validate,
 | 
			
		||||
                    'write_concern': write_concern,
 | 
			
		||||
                    'cascade': cascade
 | 
			
		||||
                    "force_insert": force_insert,
 | 
			
		||||
                    "validate": validate,
 | 
			
		||||
                    "write_concern": write_concern,
 | 
			
		||||
                    "cascade": cascade,
 | 
			
		||||
                }
 | 
			
		||||
                if cascade_kwargs:  # Allow granular control over cascades
 | 
			
		||||
                    kwargs.update(cascade_kwargs)
 | 
			
		||||
                kwargs['_refs'] = _refs
 | 
			
		||||
                kwargs["_refs"] = _refs
 | 
			
		||||
                self.cascade_save(**kwargs)
 | 
			
		||||
 | 
			
		||||
        except pymongo.errors.DuplicateKeyError as err:
 | 
			
		||||
            message = u'Tried to save duplicate unique keys (%s)'
 | 
			
		||||
            message = u"Tried to save duplicate unique keys (%s)"
 | 
			
		||||
            raise NotUniqueError(message % six.text_type(err))
 | 
			
		||||
        except pymongo.errors.OperationFailure as err:
 | 
			
		||||
            message = 'Could not save document (%s)'
 | 
			
		||||
            if re.match('^E1100[01] duplicate key', six.text_type(err)):
 | 
			
		||||
            message = "Could not save document (%s)"
 | 
			
		||||
            if re.match("^E1100[01] duplicate key", six.text_type(err)):
 | 
			
		||||
                # E11000 - duplicate key error index
 | 
			
		||||
                # E11001 - duplicate key on update
 | 
			
		||||
                message = u'Tried to save duplicate unique keys (%s)'
 | 
			
		||||
                message = u"Tried to save duplicate unique keys (%s)"
 | 
			
		||||
                raise NotUniqueError(message % six.text_type(err))
 | 
			
		||||
            raise OperationError(message % six.text_type(err))
 | 
			
		||||
 | 
			
		||||
        # Make sure we store the PK on this document now that it's saved
 | 
			
		||||
        id_field = self._meta['id_field']
 | 
			
		||||
        if created or id_field not in self._meta.get('shard_key', []):
 | 
			
		||||
        id_field = self._meta["id_field"]
 | 
			
		||||
        if created or id_field not in self._meta.get("shard_key", []):
 | 
			
		||||
            self[id_field] = self._fields[id_field].to_python(object_id)
 | 
			
		||||
 | 
			
		||||
        signals.post_save.send(self.__class__, document=self,
 | 
			
		||||
                               created=created, **signal_kwargs)
 | 
			
		||||
        signals.post_save.send(
 | 
			
		||||
            self.__class__, document=self, created=created, **signal_kwargs
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        self._clear_changed_fields()
 | 
			
		||||
        self._created = False
 | 
			
		||||
@@ -442,11 +467,12 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
 | 
			
		||||
                return wc_collection.insert_one(doc).inserted_id
 | 
			
		||||
            # insert_one will provoke UniqueError alongside save does not
 | 
			
		||||
            # therefore, it need to catch and call replace_one.
 | 
			
		||||
            if '_id' in doc:
 | 
			
		||||
            if "_id" in doc:
 | 
			
		||||
                raw_object = wc_collection.find_one_and_replace(
 | 
			
		||||
                    {'_id': doc['_id']}, doc)
 | 
			
		||||
                    {"_id": doc["_id"]}, doc
 | 
			
		||||
                )
 | 
			
		||||
                if raw_object:
 | 
			
		||||
                    return doc['_id']
 | 
			
		||||
                    return doc["_id"]
 | 
			
		||||
 | 
			
		||||
            object_id = wc_collection.insert_one(doc).inserted_id
 | 
			
		||||
 | 
			
		||||
@@ -461,9 +487,9 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
 | 
			
		||||
 | 
			
		||||
        update_doc = {}
 | 
			
		||||
        if updates:
 | 
			
		||||
            update_doc['$set'] = updates
 | 
			
		||||
            update_doc["$set"] = updates
 | 
			
		||||
        if removals:
 | 
			
		||||
            update_doc['$unset'] = removals
 | 
			
		||||
            update_doc["$unset"] = removals
 | 
			
		||||
 | 
			
		||||
        return update_doc
 | 
			
		||||
 | 
			
		||||
@@ -473,39 +499,38 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
 | 
			
		||||
        Helper method, should only be used inside save().
 | 
			
		||||
        """
 | 
			
		||||
        collection = self._get_collection()
 | 
			
		||||
        object_id = doc['_id']
 | 
			
		||||
        object_id = doc["_id"]
 | 
			
		||||
        created = False
 | 
			
		||||
 | 
			
		||||
        select_dict = {}
 | 
			
		||||
        if save_condition is not None:
 | 
			
		||||
            select_dict = transform.query(self.__class__, **save_condition)
 | 
			
		||||
 | 
			
		||||
        select_dict['_id'] = object_id
 | 
			
		||||
        select_dict["_id"] = object_id
 | 
			
		||||
 | 
			
		||||
        # Need to add shard key to query, or you get an error
 | 
			
		||||
        shard_key = self._meta.get('shard_key', tuple())
 | 
			
		||||
        shard_key = self._meta.get("shard_key", tuple())
 | 
			
		||||
        for k in shard_key:
 | 
			
		||||
            path = self._lookup_field(k.split('.'))
 | 
			
		||||
            path = self._lookup_field(k.split("."))
 | 
			
		||||
            actual_key = [p.db_field for p in path]
 | 
			
		||||
            val = doc
 | 
			
		||||
            for ak in actual_key:
 | 
			
		||||
                val = val[ak]
 | 
			
		||||
            select_dict['.'.join(actual_key)] = val
 | 
			
		||||
            select_dict[".".join(actual_key)] = val
 | 
			
		||||
 | 
			
		||||
        update_doc = self._get_update_doc()
 | 
			
		||||
        if update_doc:
 | 
			
		||||
            upsert = save_condition is None
 | 
			
		||||
            with set_write_concern(collection, write_concern) as wc_collection:
 | 
			
		||||
                last_error = wc_collection.update_one(
 | 
			
		||||
                    select_dict,
 | 
			
		||||
                    update_doc,
 | 
			
		||||
                    upsert=upsert
 | 
			
		||||
                    select_dict, update_doc, upsert=upsert
 | 
			
		||||
                ).raw_result
 | 
			
		||||
            if not upsert and last_error['n'] == 0:
 | 
			
		||||
                raise SaveConditionError('Race condition preventing'
 | 
			
		||||
                                         ' document update detected')
 | 
			
		||||
            if not upsert and last_error["n"] == 0:
 | 
			
		||||
                raise SaveConditionError(
 | 
			
		||||
                    "Race condition preventing document update detected"
 | 
			
		||||
                )
 | 
			
		||||
            if last_error is not None:
 | 
			
		||||
                updated_existing = last_error.get('updatedExisting')
 | 
			
		||||
                updated_existing = last_error.get("updatedExisting")
 | 
			
		||||
                if updated_existing is False:
 | 
			
		||||
                    created = True
 | 
			
		||||
                    # !!! This is bad, means we accidentally created a new,
 | 
			
		||||
@@ -518,21 +543,20 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
 | 
			
		||||
        """Recursively save any references and generic references on the
 | 
			
		||||
        document.
 | 
			
		||||
        """
 | 
			
		||||
        _refs = kwargs.get('_refs') or []
 | 
			
		||||
        _refs = kwargs.get("_refs") or []
 | 
			
		||||
 | 
			
		||||
        ReferenceField = _import_class('ReferenceField')
 | 
			
		||||
        GenericReferenceField = _import_class('GenericReferenceField')
 | 
			
		||||
        ReferenceField = _import_class("ReferenceField")
 | 
			
		||||
        GenericReferenceField = _import_class("GenericReferenceField")
 | 
			
		||||
 | 
			
		||||
        for name, cls in self._fields.items():
 | 
			
		||||
            if not isinstance(cls, (ReferenceField,
 | 
			
		||||
                                    GenericReferenceField)):
 | 
			
		||||
            if not isinstance(cls, (ReferenceField, GenericReferenceField)):
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            ref = self._data.get(name)
 | 
			
		||||
            if not ref or isinstance(ref, DBRef):
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            if not getattr(ref, '_changed_fields', True):
 | 
			
		||||
            if not getattr(ref, "_changed_fields", True):
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            ref_id = "%s,%s" % (ref.__class__.__name__, str(ref._data))
 | 
			
		||||
@@ -545,7 +569,7 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
 | 
			
		||||
    @property
 | 
			
		||||
    def _qs(self):
 | 
			
		||||
        """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())
 | 
			
		||||
        return self.__objects
 | 
			
		||||
 | 
			
		||||
@@ -558,15 +582,15 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
 | 
			
		||||
        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())
 | 
			
		||||
        select_dict = {"pk": self.pk}
 | 
			
		||||
        shard_key = self.__class__._meta.get("shard_key", tuple())
 | 
			
		||||
        for k in shard_key:
 | 
			
		||||
            path = self._lookup_field(k.split('.'))
 | 
			
		||||
            path = self._lookup_field(k.split("."))
 | 
			
		||||
            actual_key = [p.db_field for p in path]
 | 
			
		||||
            val = self
 | 
			
		||||
            for ak in actual_key:
 | 
			
		||||
                val = getattr(val, ak)
 | 
			
		||||
            select_dict['__'.join(actual_key)] = val
 | 
			
		||||
            select_dict["__".join(actual_key)] = val
 | 
			
		||||
        return select_dict
 | 
			
		||||
 | 
			
		||||
    def update(self, **kwargs):
 | 
			
		||||
@@ -577,14 +601,13 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
 | 
			
		||||
        been saved.
 | 
			
		||||
        """
 | 
			
		||||
        if self.pk is None:
 | 
			
		||||
            if kwargs.get('upsert', False):
 | 
			
		||||
            if kwargs.get("upsert", False):
 | 
			
		||||
                query = self.to_mongo()
 | 
			
		||||
                if '_cls' in query:
 | 
			
		||||
                    del query['_cls']
 | 
			
		||||
                if "_cls" in query:
 | 
			
		||||
                    del query["_cls"]
 | 
			
		||||
                return self._qs.filter(**query).update_one(**kwargs)
 | 
			
		||||
            else:
 | 
			
		||||
                raise OperationError(
 | 
			
		||||
                    'attempt to update a document not yet saved')
 | 
			
		||||
                raise OperationError("attempt to update a document not yet saved")
 | 
			
		||||
 | 
			
		||||
        # Need to add shard key to query, or you get an error
 | 
			
		||||
        return self._qs.filter(**self._object_key).update_one(**kwargs)
 | 
			
		||||
@@ -608,16 +631,17 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
 | 
			
		||||
        signals.pre_delete.send(self.__class__, document=self, **signal_kwargs)
 | 
			
		||||
 | 
			
		||||
        # Delete FileFields separately
 | 
			
		||||
        FileField = _import_class('FileField')
 | 
			
		||||
        FileField = _import_class("FileField")
 | 
			
		||||
        for name, field in iteritems(self._fields):
 | 
			
		||||
            if isinstance(field, FileField):
 | 
			
		||||
                getattr(self, name).delete()
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            self._qs.filter(
 | 
			
		||||
                **self._object_key).delete(write_concern=write_concern, _from_doc_delete=True)
 | 
			
		||||
            self._qs.filter(**self._object_key).delete(
 | 
			
		||||
                write_concern=write_concern, _from_doc_delete=True
 | 
			
		||||
            )
 | 
			
		||||
        except pymongo.errors.OperationFailure as err:
 | 
			
		||||
            message = u'Could not delete document (%s)' % err.message
 | 
			
		||||
            message = u"Could not delete document (%s)" % err.message
 | 
			
		||||
            raise OperationError(message)
 | 
			
		||||
        signals.post_delete.send(self.__class__, document=self, **signal_kwargs)
 | 
			
		||||
 | 
			
		||||
@@ -686,7 +710,7 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
 | 
			
		||||
 | 
			
		||||
        .. versionadded:: 0.5
 | 
			
		||||
        """
 | 
			
		||||
        DeReference = _import_class('DeReference')
 | 
			
		||||
        DeReference = _import_class("DeReference")
 | 
			
		||||
        DeReference()([self], max_depth + 1)
 | 
			
		||||
        return self
 | 
			
		||||
 | 
			
		||||
@@ -704,20 +728,24 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
 | 
			
		||||
        if fields and isinstance(fields[0], int):
 | 
			
		||||
            max_depth = fields[0]
 | 
			
		||||
            fields = fields[1:]
 | 
			
		||||
        elif 'max_depth' in kwargs:
 | 
			
		||||
            max_depth = kwargs['max_depth']
 | 
			
		||||
        elif "max_depth" in kwargs:
 | 
			
		||||
            max_depth = kwargs["max_depth"]
 | 
			
		||||
 | 
			
		||||
        if self.pk is None:
 | 
			
		||||
            raise self.DoesNotExist('Document does not exist')
 | 
			
		||||
            raise self.DoesNotExist("Document does not exist")
 | 
			
		||||
 | 
			
		||||
        obj = self._qs.read_preference(ReadPreference.PRIMARY).filter(
 | 
			
		||||
            **self._object_key).only(*fields).limit(
 | 
			
		||||
            1).select_related(max_depth=max_depth)
 | 
			
		||||
        obj = (
 | 
			
		||||
            self._qs.read_preference(ReadPreference.PRIMARY)
 | 
			
		||||
            .filter(**self._object_key)
 | 
			
		||||
            .only(*fields)
 | 
			
		||||
            .limit(1)
 | 
			
		||||
            .select_related(max_depth=max_depth)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        if obj:
 | 
			
		||||
            obj = obj[0]
 | 
			
		||||
        else:
 | 
			
		||||
            raise self.DoesNotExist('Document does not exist')
 | 
			
		||||
            raise self.DoesNotExist("Document does not exist")
 | 
			
		||||
        for field in obj._data:
 | 
			
		||||
            if not fields or field in fields:
 | 
			
		||||
                try:
 | 
			
		||||
@@ -733,9 +761,11 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
 | 
			
		||||
                        # i.e. obj.update(unset__field=1) followed by obj.reload()
 | 
			
		||||
                        delattr(self, field)
 | 
			
		||||
 | 
			
		||||
        self._changed_fields = list(
 | 
			
		||||
            set(self._changed_fields) - set(fields)
 | 
			
		||||
        ) if fields else obj._changed_fields
 | 
			
		||||
        self._changed_fields = (
 | 
			
		||||
            list(set(self._changed_fields) - set(fields))
 | 
			
		||||
            if fields
 | 
			
		||||
            else obj._changed_fields
 | 
			
		||||
        )
 | 
			
		||||
        self._created = False
 | 
			
		||||
        return self
 | 
			
		||||
 | 
			
		||||
@@ -761,7 +791,7 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
 | 
			
		||||
        """Returns an instance of :class:`~bson.dbref.DBRef` useful in
 | 
			
		||||
        `__raw__` queries."""
 | 
			
		||||
        if self.pk is None:
 | 
			
		||||
            msg = 'Only saved documents can have a valid dbref'
 | 
			
		||||
            msg = "Only saved documents can have a valid dbref"
 | 
			
		||||
            raise OperationError(msg)
 | 
			
		||||
        return DBRef(self.__class__._get_collection_name(), self.pk)
 | 
			
		||||
 | 
			
		||||
@@ -770,18 +800,22 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
 | 
			
		||||
        """This method registers the delete rules to apply when removing this
 | 
			
		||||
        object.
 | 
			
		||||
        """
 | 
			
		||||
        classes = [get_document(class_name)
 | 
			
		||||
                   for class_name in cls._subclasses
 | 
			
		||||
                   if class_name != cls.__name__] + [cls]
 | 
			
		||||
        documents = [get_document(class_name)
 | 
			
		||||
                     for class_name in document_cls._subclasses
 | 
			
		||||
                     if class_name != document_cls.__name__] + [document_cls]
 | 
			
		||||
        classes = [
 | 
			
		||||
            get_document(class_name)
 | 
			
		||||
            for class_name in cls._subclasses
 | 
			
		||||
            if class_name != cls.__name__
 | 
			
		||||
        ] + [cls]
 | 
			
		||||
        documents = [
 | 
			
		||||
            get_document(class_name)
 | 
			
		||||
            for class_name in document_cls._subclasses
 | 
			
		||||
            if class_name != document_cls.__name__
 | 
			
		||||
        ] + [document_cls]
 | 
			
		||||
 | 
			
		||||
        for klass in classes:
 | 
			
		||||
            for document_cls in documents:
 | 
			
		||||
                delete_rules = klass._meta.get('delete_rules') or {}
 | 
			
		||||
                delete_rules = klass._meta.get("delete_rules") or {}
 | 
			
		||||
                delete_rules[(document_cls, field_name)] = rule
 | 
			
		||||
                klass._meta['delete_rules'] = delete_rules
 | 
			
		||||
                klass._meta["delete_rules"] = delete_rules
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def drop_collection(cls):
 | 
			
		||||
@@ -796,8 +830,9 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
 | 
			
		||||
        """
 | 
			
		||||
        coll_name = cls._get_collection_name()
 | 
			
		||||
        if not coll_name:
 | 
			
		||||
            raise OperationError('Document %s has no collection defined '
 | 
			
		||||
                                 '(is it abstract ?)' % cls)
 | 
			
		||||
            raise OperationError(
 | 
			
		||||
                "Document %s has no collection defined (is it abstract ?)" % cls
 | 
			
		||||
            )
 | 
			
		||||
        cls._collection = None
 | 
			
		||||
        db = cls._get_db()
 | 
			
		||||
        db.drop_collection(coll_name)
 | 
			
		||||
@@ -813,19 +848,18 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
 | 
			
		||||
        """
 | 
			
		||||
        index_spec = cls._build_index_spec(keys)
 | 
			
		||||
        index_spec = index_spec.copy()
 | 
			
		||||
        fields = index_spec.pop('fields')
 | 
			
		||||
        drop_dups = kwargs.get('drop_dups', False)
 | 
			
		||||
        fields = index_spec.pop("fields")
 | 
			
		||||
        drop_dups = kwargs.get("drop_dups", False)
 | 
			
		||||
        if drop_dups:
 | 
			
		||||
            msg = 'drop_dups is deprecated and is removed when using PyMongo 3+.'
 | 
			
		||||
            msg = "drop_dups is deprecated and is removed when using PyMongo 3+."
 | 
			
		||||
            warnings.warn(msg, DeprecationWarning)
 | 
			
		||||
        index_spec['background'] = background
 | 
			
		||||
        index_spec["background"] = background
 | 
			
		||||
        index_spec.update(kwargs)
 | 
			
		||||
 | 
			
		||||
        return cls._get_collection().create_index(fields, **index_spec)
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def ensure_index(cls, key_or_list, drop_dups=False, background=False,
 | 
			
		||||
                     **kwargs):
 | 
			
		||||
    def ensure_index(cls, key_or_list, drop_dups=False, background=False, **kwargs):
 | 
			
		||||
        """Ensure that the given indexes are in place. Deprecated in favour
 | 
			
		||||
        of create_index.
 | 
			
		||||
 | 
			
		||||
@@ -837,7 +871,7 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
 | 
			
		||||
            will be removed if PyMongo3+ is used
 | 
			
		||||
        """
 | 
			
		||||
        if drop_dups:
 | 
			
		||||
            msg = 'drop_dups is deprecated and is removed when using PyMongo 3+.'
 | 
			
		||||
            msg = "drop_dups is deprecated and is removed when using PyMongo 3+."
 | 
			
		||||
            warnings.warn(msg, DeprecationWarning)
 | 
			
		||||
        return cls.create_index(key_or_list, background=background, **kwargs)
 | 
			
		||||
 | 
			
		||||
@@ -850,12 +884,12 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
 | 
			
		||||
        .. note:: You can disable automatic index creation by setting
 | 
			
		||||
                  `auto_create_index` to False in the documents meta data
 | 
			
		||||
        """
 | 
			
		||||
        background = cls._meta.get('index_background', False)
 | 
			
		||||
        drop_dups = cls._meta.get('index_drop_dups', False)
 | 
			
		||||
        index_opts = cls._meta.get('index_opts') or {}
 | 
			
		||||
        index_cls = cls._meta.get('index_cls', True)
 | 
			
		||||
        background = cls._meta.get("index_background", False)
 | 
			
		||||
        drop_dups = cls._meta.get("index_drop_dups", False)
 | 
			
		||||
        index_opts = cls._meta.get("index_opts") or {}
 | 
			
		||||
        index_cls = cls._meta.get("index_cls", True)
 | 
			
		||||
        if drop_dups:
 | 
			
		||||
            msg = 'drop_dups is deprecated and is removed when using PyMongo 3+.'
 | 
			
		||||
            msg = "drop_dups is deprecated and is removed when using PyMongo 3+."
 | 
			
		||||
            warnings.warn(msg, DeprecationWarning)
 | 
			
		||||
 | 
			
		||||
        collection = cls._get_collection()
 | 
			
		||||
@@ -871,40 +905,39 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
 | 
			
		||||
        cls_indexed = False
 | 
			
		||||
 | 
			
		||||
        # Ensure document-defined indexes are created
 | 
			
		||||
        if cls._meta['index_specs']:
 | 
			
		||||
            index_spec = cls._meta['index_specs']
 | 
			
		||||
        if cls._meta["index_specs"]:
 | 
			
		||||
            index_spec = cls._meta["index_specs"]
 | 
			
		||||
            for spec in index_spec:
 | 
			
		||||
                spec = spec.copy()
 | 
			
		||||
                fields = spec.pop('fields')
 | 
			
		||||
                fields = spec.pop("fields")
 | 
			
		||||
                cls_indexed = cls_indexed or includes_cls(fields)
 | 
			
		||||
                opts = index_opts.copy()
 | 
			
		||||
                opts.update(spec)
 | 
			
		||||
 | 
			
		||||
                # we shouldn't pass 'cls' to the collection.ensureIndex options
 | 
			
		||||
                # because of https://jira.mongodb.org/browse/SERVER-769
 | 
			
		||||
                if 'cls' in opts:
 | 
			
		||||
                    del opts['cls']
 | 
			
		||||
                if "cls" in opts:
 | 
			
		||||
                    del opts["cls"]
 | 
			
		||||
 | 
			
		||||
                collection.create_index(fields, background=background, **opts)
 | 
			
		||||
 | 
			
		||||
        # If _cls is being used (for polymorphism), it needs an index,
 | 
			
		||||
        # only if another index doesn't begin with _cls
 | 
			
		||||
        if index_cls and not cls_indexed and cls._meta.get('allow_inheritance'):
 | 
			
		||||
        if index_cls and not cls_indexed and cls._meta.get("allow_inheritance"):
 | 
			
		||||
 | 
			
		||||
            # we shouldn't pass 'cls' to the collection.ensureIndex options
 | 
			
		||||
            # because of https://jira.mongodb.org/browse/SERVER-769
 | 
			
		||||
            if 'cls' in index_opts:
 | 
			
		||||
                del index_opts['cls']
 | 
			
		||||
            if "cls" in index_opts:
 | 
			
		||||
                del index_opts["cls"]
 | 
			
		||||
 | 
			
		||||
            collection.create_index('_cls', background=background,
 | 
			
		||||
                                    **index_opts)
 | 
			
		||||
            collection.create_index("_cls", background=background, **index_opts)
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def list_indexes(cls):
 | 
			
		||||
        """ Lists all of the indexes that should be created for given
 | 
			
		||||
        collection. It includes all the indexes from super- and sub-classes.
 | 
			
		||||
        """
 | 
			
		||||
        if cls._meta.get('abstract'):
 | 
			
		||||
        if cls._meta.get("abstract"):
 | 
			
		||||
            return []
 | 
			
		||||
 | 
			
		||||
        # get all the base classes, subclasses and siblings
 | 
			
		||||
@@ -912,22 +945,27 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
 | 
			
		||||
 | 
			
		||||
        def get_classes(cls):
 | 
			
		||||
 | 
			
		||||
            if (cls not in classes and
 | 
			
		||||
                    isinstance(cls, TopLevelDocumentMetaclass)):
 | 
			
		||||
            if cls not in classes and isinstance(cls, TopLevelDocumentMetaclass):
 | 
			
		||||
                classes.append(cls)
 | 
			
		||||
 | 
			
		||||
            for base_cls in cls.__bases__:
 | 
			
		||||
                if (isinstance(base_cls, TopLevelDocumentMetaclass) and
 | 
			
		||||
                        base_cls != Document and
 | 
			
		||||
                        not base_cls._meta.get('abstract') and
 | 
			
		||||
                        base_cls._get_collection().full_name == cls._get_collection().full_name and
 | 
			
		||||
                        base_cls not in classes):
 | 
			
		||||
                if (
 | 
			
		||||
                    isinstance(base_cls, TopLevelDocumentMetaclass)
 | 
			
		||||
                    and base_cls != Document
 | 
			
		||||
                    and not base_cls._meta.get("abstract")
 | 
			
		||||
                    and base_cls._get_collection().full_name
 | 
			
		||||
                    == cls._get_collection().full_name
 | 
			
		||||
                    and base_cls not in classes
 | 
			
		||||
                ):
 | 
			
		||||
                    classes.append(base_cls)
 | 
			
		||||
                    get_classes(base_cls)
 | 
			
		||||
            for subclass in cls.__subclasses__():
 | 
			
		||||
                if (isinstance(base_cls, TopLevelDocumentMetaclass) and
 | 
			
		||||
                        subclass._get_collection().full_name == cls._get_collection().full_name and
 | 
			
		||||
                        subclass not in classes):
 | 
			
		||||
                if (
 | 
			
		||||
                    isinstance(base_cls, TopLevelDocumentMetaclass)
 | 
			
		||||
                    and subclass._get_collection().full_name
 | 
			
		||||
                    == cls._get_collection().full_name
 | 
			
		||||
                    and subclass not in classes
 | 
			
		||||
                ):
 | 
			
		||||
                    classes.append(subclass)
 | 
			
		||||
                    get_classes(subclass)
 | 
			
		||||
 | 
			
		||||
@@ -937,11 +975,11 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
 | 
			
		||||
        def get_indexes_spec(cls):
 | 
			
		||||
            indexes = []
 | 
			
		||||
 | 
			
		||||
            if cls._meta['index_specs']:
 | 
			
		||||
                index_spec = cls._meta['index_specs']
 | 
			
		||||
            if cls._meta["index_specs"]:
 | 
			
		||||
                index_spec = cls._meta["index_specs"]
 | 
			
		||||
                for spec in index_spec:
 | 
			
		||||
                    spec = spec.copy()
 | 
			
		||||
                    fields = spec.pop('fields')
 | 
			
		||||
                    fields = spec.pop("fields")
 | 
			
		||||
                    indexes.append(fields)
 | 
			
		||||
            return indexes
 | 
			
		||||
 | 
			
		||||
@@ -952,10 +990,10 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
 | 
			
		||||
                    indexes.append(index)
 | 
			
		||||
 | 
			
		||||
        # finish up by appending { '_id': 1 } and { '_cls': 1 }, if needed
 | 
			
		||||
        if [(u'_id', 1)] not in indexes:
 | 
			
		||||
            indexes.append([(u'_id', 1)])
 | 
			
		||||
        if cls._meta.get('index_cls', True) and cls._meta.get('allow_inheritance'):
 | 
			
		||||
            indexes.append([(u'_cls', 1)])
 | 
			
		||||
        if [(u"_id", 1)] not in indexes:
 | 
			
		||||
            indexes.append([(u"_id", 1)])
 | 
			
		||||
        if cls._meta.get("index_cls", True) and cls._meta.get("allow_inheritance"):
 | 
			
		||||
            indexes.append([(u"_cls", 1)])
 | 
			
		||||
 | 
			
		||||
        return indexes
 | 
			
		||||
 | 
			
		||||
@@ -969,27 +1007,26 @@ class Document(six.with_metaclass(TopLevelDocumentMetaclass, BaseDocument)):
 | 
			
		||||
 | 
			
		||||
        existing = []
 | 
			
		||||
        for info in cls._get_collection().index_information().values():
 | 
			
		||||
            if '_fts' in info['key'][0]:
 | 
			
		||||
                index_type = info['key'][0][1]
 | 
			
		||||
                text_index_fields = info.get('weights').keys()
 | 
			
		||||
                existing.append(
 | 
			
		||||
                    [(key, index_type) for key in text_index_fields])
 | 
			
		||||
            if "_fts" in info["key"][0]:
 | 
			
		||||
                index_type = info["key"][0][1]
 | 
			
		||||
                text_index_fields = info.get("weights").keys()
 | 
			
		||||
                existing.append([(key, index_type) for key in text_index_fields])
 | 
			
		||||
            else:
 | 
			
		||||
                existing.append(info['key'])
 | 
			
		||||
                existing.append(info["key"])
 | 
			
		||||
        missing = [index for index in required if index not in existing]
 | 
			
		||||
        extra = [index for index in existing if index not in required]
 | 
			
		||||
 | 
			
		||||
        # if { _cls: 1 } is missing, make sure it's *really* necessary
 | 
			
		||||
        if [(u'_cls', 1)] in missing:
 | 
			
		||||
        if [(u"_cls", 1)] in missing:
 | 
			
		||||
            cls_obsolete = False
 | 
			
		||||
            for index in existing:
 | 
			
		||||
                if includes_cls(index) and index not in extra:
 | 
			
		||||
                    cls_obsolete = True
 | 
			
		||||
                    break
 | 
			
		||||
            if cls_obsolete:
 | 
			
		||||
                missing.remove([(u'_cls', 1)])
 | 
			
		||||
                missing.remove([(u"_cls", 1)])
 | 
			
		||||
 | 
			
		||||
        return {'missing': missing, 'extra': extra}
 | 
			
		||||
        return {"missing": missing, "extra": extra}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DynamicDocument(six.with_metaclass(TopLevelDocumentMetaclass, Document)):
 | 
			
		||||
@@ -1074,17 +1111,16 @@ class MapReduceDocument(object):
 | 
			
		||||
        """Lazy-load the object referenced by ``self.key``. ``self.key``
 | 
			
		||||
        should be the ``primary_key``.
 | 
			
		||||
        """
 | 
			
		||||
        id_field = self._document()._meta['id_field']
 | 
			
		||||
        id_field = self._document()._meta["id_field"]
 | 
			
		||||
        id_field_type = type(id_field)
 | 
			
		||||
 | 
			
		||||
        if not isinstance(self.key, id_field_type):
 | 
			
		||||
            try:
 | 
			
		||||
                self.key = id_field_type(self.key)
 | 
			
		||||
            except Exception:
 | 
			
		||||
                raise Exception('Could not cast key as %s' %
 | 
			
		||||
                                id_field_type.__name__)
 | 
			
		||||
                raise Exception("Could not cast key as %s" % id_field_type.__name__)
 | 
			
		||||
 | 
			
		||||
        if not hasattr(self, '_key_object'):
 | 
			
		||||
        if not hasattr(self, "_key_object"):
 | 
			
		||||
            self._key_object = self._document.objects.with_id(self.key)
 | 
			
		||||
            return self._key_object
 | 
			
		||||
        return self._key_object
 | 
			
		||||
 
 | 
			
		||||
@@ -3,10 +3,20 @@ from collections import defaultdict
 | 
			
		||||
import six
 | 
			
		||||
from six import iteritems
 | 
			
		||||
 | 
			
		||||
__all__ = ('NotRegistered', 'InvalidDocumentError', 'LookUpError',
 | 
			
		||||
           'DoesNotExist', 'MultipleObjectsReturned', 'InvalidQueryError',
 | 
			
		||||
           'OperationError', 'NotUniqueError', 'FieldDoesNotExist',
 | 
			
		||||
           'ValidationError', 'SaveConditionError', 'DeprecatedError')
 | 
			
		||||
__all__ = (
 | 
			
		||||
    "NotRegistered",
 | 
			
		||||
    "InvalidDocumentError",
 | 
			
		||||
    "LookUpError",
 | 
			
		||||
    "DoesNotExist",
 | 
			
		||||
    "MultipleObjectsReturned",
 | 
			
		||||
    "InvalidQueryError",
 | 
			
		||||
    "OperationError",
 | 
			
		||||
    "NotUniqueError",
 | 
			
		||||
    "FieldDoesNotExist",
 | 
			
		||||
    "ValidationError",
 | 
			
		||||
    "SaveConditionError",
 | 
			
		||||
    "DeprecatedError",
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NotRegistered(Exception):
 | 
			
		||||
@@ -71,25 +81,25 @@ class ValidationError(AssertionError):
 | 
			
		||||
    field_name = None
 | 
			
		||||
    _message = None
 | 
			
		||||
 | 
			
		||||
    def __init__(self, message='', **kwargs):
 | 
			
		||||
    def __init__(self, message="", **kwargs):
 | 
			
		||||
        super(ValidationError, self).__init__(message)
 | 
			
		||||
        self.errors = kwargs.get('errors', {})
 | 
			
		||||
        self.field_name = kwargs.get('field_name')
 | 
			
		||||
        self.errors = kwargs.get("errors", {})
 | 
			
		||||
        self.field_name = kwargs.get("field_name")
 | 
			
		||||
        self.message = message
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return six.text_type(self.message)
 | 
			
		||||
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
        return '%s(%s,)' % (self.__class__.__name__, self.message)
 | 
			
		||||
        return "%s(%s,)" % (self.__class__.__name__, self.message)
 | 
			
		||||
 | 
			
		||||
    def __getattribute__(self, name):
 | 
			
		||||
        message = super(ValidationError, self).__getattribute__(name)
 | 
			
		||||
        if name == 'message':
 | 
			
		||||
        if name == "message":
 | 
			
		||||
            if self.field_name:
 | 
			
		||||
                message = '%s' % message
 | 
			
		||||
                message = "%s" % message
 | 
			
		||||
            if self.errors:
 | 
			
		||||
                message = '%s(%s)' % (message, self._format_errors())
 | 
			
		||||
                message = "%s(%s)" % (message, self._format_errors())
 | 
			
		||||
        return message
 | 
			
		||||
 | 
			
		||||
    def _get_message(self):
 | 
			
		||||
@@ -128,22 +138,22 @@ class ValidationError(AssertionError):
 | 
			
		||||
    def _format_errors(self):
 | 
			
		||||
        """Returns a string listing all errors within a document"""
 | 
			
		||||
 | 
			
		||||
        def generate_key(value, prefix=''):
 | 
			
		||||
        def generate_key(value, prefix=""):
 | 
			
		||||
            if isinstance(value, list):
 | 
			
		||||
                value = ' '.join([generate_key(k) for k in value])
 | 
			
		||||
                value = " ".join([generate_key(k) for k in value])
 | 
			
		||||
            elif isinstance(value, dict):
 | 
			
		||||
                value = ' '.join(
 | 
			
		||||
                    [generate_key(v, k) for k, v in iteritems(value)])
 | 
			
		||||
                value = " ".join([generate_key(v, k) for k, v in iteritems(value)])
 | 
			
		||||
 | 
			
		||||
            results = '%s.%s' % (prefix, value) if prefix else value
 | 
			
		||||
            results = "%s.%s" % (prefix, value) if prefix else value
 | 
			
		||||
            return results
 | 
			
		||||
 | 
			
		||||
        error_dict = defaultdict(list)
 | 
			
		||||
        for k, v in iteritems(self.to_dict()):
 | 
			
		||||
            error_dict[generate_key(v)].append(k)
 | 
			
		||||
        return ' '.join(['%s: %s' % (k, v) for k, v in iteritems(error_dict)])
 | 
			
		||||
        return " ".join(["%s: %s" % (k, v) for k, v in iteritems(error_dict)])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DeprecatedError(Exception):
 | 
			
		||||
    """Raise when a user uses a feature that has been Deprecated"""
 | 
			
		||||
 | 
			
		||||
    pass
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -15,5 +15,5 @@ def get_mongodb_version():
 | 
			
		||||
 | 
			
		||||
    :return: tuple(int, int)
 | 
			
		||||
    """
 | 
			
		||||
    version_list = get_connection().server_info()['versionArray'][:2]     # e.g: (3, 2)
 | 
			
		||||
    version_list = get_connection().server_info()["versionArray"][:2]  # e.g: (3, 2)
 | 
			
		||||
    return tuple(version_list)
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,6 @@ def list_collection_names(db, include_system_collections=False):
 | 
			
		||||
        collections = db.collection_names()
 | 
			
		||||
 | 
			
		||||
    if not include_system_collections:
 | 
			
		||||
        collections = [c for c in collections if not c.startswith('system.')]
 | 
			
		||||
        collections = [c for c in collections if not c.startswith("system.")]
 | 
			
		||||
 | 
			
		||||
    return collections
 | 
			
		||||
 
 | 
			
		||||
@@ -7,11 +7,22 @@ from mongoengine.queryset.visitor import *
 | 
			
		||||
 | 
			
		||||
# Expose just the public subset of all imported objects and constants.
 | 
			
		||||
__all__ = (
 | 
			
		||||
    'QuerySet', 'QuerySetNoCache', 'Q', 'queryset_manager', 'QuerySetManager',
 | 
			
		||||
    'QueryFieldList', 'DO_NOTHING', 'NULLIFY', 'CASCADE', 'DENY', 'PULL',
 | 
			
		||||
 | 
			
		||||
    "QuerySet",
 | 
			
		||||
    "QuerySetNoCache",
 | 
			
		||||
    "Q",
 | 
			
		||||
    "queryset_manager",
 | 
			
		||||
    "QuerySetManager",
 | 
			
		||||
    "QueryFieldList",
 | 
			
		||||
    "DO_NOTHING",
 | 
			
		||||
    "NULLIFY",
 | 
			
		||||
    "CASCADE",
 | 
			
		||||
    "DENY",
 | 
			
		||||
    "PULL",
 | 
			
		||||
    # Errors that might be related to a queryset, mostly here for backward
 | 
			
		||||
    # compatibility
 | 
			
		||||
    'DoesNotExist', 'InvalidQueryError', 'MultipleObjectsReturned',
 | 
			
		||||
    'NotUniqueError', 'OperationError',
 | 
			
		||||
    "DoesNotExist",
 | 
			
		||||
    "InvalidQueryError",
 | 
			
		||||
    "MultipleObjectsReturned",
 | 
			
		||||
    "NotUniqueError",
 | 
			
		||||
    "OperationError",
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,12 +1,15 @@
 | 
			
		||||
__all__ = ('QueryFieldList',)
 | 
			
		||||
__all__ = ("QueryFieldList",)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class QueryFieldList(object):
 | 
			
		||||
    """Object that handles combinations of .only() and .exclude() calls"""
 | 
			
		||||
 | 
			
		||||
    ONLY = 1
 | 
			
		||||
    EXCLUDE = 0
 | 
			
		||||
 | 
			
		||||
    def __init__(self, fields=None, value=ONLY, always_include=None, _only_called=False):
 | 
			
		||||
    def __init__(
 | 
			
		||||
        self, fields=None, value=ONLY, always_include=None, _only_called=False
 | 
			
		||||
    ):
 | 
			
		||||
        """The QueryFieldList builder
 | 
			
		||||
 | 
			
		||||
        :param fields: A list of fields used in `.only()` or `.exclude()`
 | 
			
		||||
@@ -49,7 +52,7 @@ class QueryFieldList(object):
 | 
			
		||||
            self.fields = f.fields - self.fields
 | 
			
		||||
            self._clean_slice()
 | 
			
		||||
 | 
			
		||||
        if '_id' in f.fields:
 | 
			
		||||
        if "_id" in f.fields:
 | 
			
		||||
            self._id = f.value
 | 
			
		||||
 | 
			
		||||
        if self.always_include:
 | 
			
		||||
@@ -59,7 +62,7 @@ class QueryFieldList(object):
 | 
			
		||||
            else:
 | 
			
		||||
                self.fields -= self.always_include
 | 
			
		||||
 | 
			
		||||
        if getattr(f, '_only_called', False):
 | 
			
		||||
        if getattr(f, "_only_called", False):
 | 
			
		||||
            self._only_called = True
 | 
			
		||||
        return self
 | 
			
		||||
 | 
			
		||||
@@ -73,7 +76,7 @@ class QueryFieldList(object):
 | 
			
		||||
        if self.slice:
 | 
			
		||||
            field_list.update(self.slice)
 | 
			
		||||
        if self._id is not None:
 | 
			
		||||
            field_list['_id'] = self._id
 | 
			
		||||
            field_list["_id"] = self._id
 | 
			
		||||
        return field_list
 | 
			
		||||
 | 
			
		||||
    def reset(self):
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
from functools import partial
 | 
			
		||||
from mongoengine.queryset.queryset import QuerySet
 | 
			
		||||
 | 
			
		||||
__all__ = ('queryset_manager', 'QuerySetManager')
 | 
			
		||||
__all__ = ("queryset_manager", "QuerySetManager")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class QuerySetManager(object):
 | 
			
		||||
@@ -33,7 +33,7 @@ class QuerySetManager(object):
 | 
			
		||||
            return self
 | 
			
		||||
 | 
			
		||||
        # owner is the document that contains the QuerySetManager
 | 
			
		||||
        queryset_class = owner._meta.get('queryset_class', self.default)
 | 
			
		||||
        queryset_class = owner._meta.get("queryset_class", self.default)
 | 
			
		||||
        queryset = queryset_class(owner, owner._get_collection())
 | 
			
		||||
        if self.get_queryset:
 | 
			
		||||
            arg_count = self.get_queryset.__code__.co_argcount
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,24 @@
 | 
			
		||||
import six
 | 
			
		||||
 | 
			
		||||
from mongoengine.errors import OperationError
 | 
			
		||||
from mongoengine.queryset.base import (BaseQuerySet, CASCADE, DENY, DO_NOTHING,
 | 
			
		||||
                                       NULLIFY, PULL)
 | 
			
		||||
from mongoengine.queryset.base import (
 | 
			
		||||
    BaseQuerySet,
 | 
			
		||||
    CASCADE,
 | 
			
		||||
    DENY,
 | 
			
		||||
    DO_NOTHING,
 | 
			
		||||
    NULLIFY,
 | 
			
		||||
    PULL,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
__all__ = ('QuerySet', 'QuerySetNoCache', 'DO_NOTHING', 'NULLIFY', 'CASCADE',
 | 
			
		||||
           'DENY', 'PULL')
 | 
			
		||||
__all__ = (
 | 
			
		||||
    "QuerySet",
 | 
			
		||||
    "QuerySetNoCache",
 | 
			
		||||
    "DO_NOTHING",
 | 
			
		||||
    "NULLIFY",
 | 
			
		||||
    "CASCADE",
 | 
			
		||||
    "DENY",
 | 
			
		||||
    "PULL",
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
# The maximum number of items to display in a QuerySet.__repr__
 | 
			
		||||
REPR_OUTPUT_SIZE = 20
 | 
			
		||||
@@ -57,12 +70,12 @@ class QuerySet(BaseQuerySet):
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
        """Provide a string representation of the QuerySet"""
 | 
			
		||||
        if self._iter:
 | 
			
		||||
            return '.. queryset mid-iteration ..'
 | 
			
		||||
            return ".. queryset mid-iteration .."
 | 
			
		||||
 | 
			
		||||
        self._populate_cache()
 | 
			
		||||
        data = self._result_cache[:REPR_OUTPUT_SIZE + 1]
 | 
			
		||||
        data = self._result_cache[: REPR_OUTPUT_SIZE + 1]
 | 
			
		||||
        if len(data) > REPR_OUTPUT_SIZE:
 | 
			
		||||
            data[-1] = '...(remaining elements truncated)...'
 | 
			
		||||
            data[-1] = "...(remaining elements truncated)..."
 | 
			
		||||
        return repr(data)
 | 
			
		||||
 | 
			
		||||
    def _iter_results(self):
 | 
			
		||||
@@ -143,10 +156,9 @@ class QuerySet(BaseQuerySet):
 | 
			
		||||
        .. versionadded:: 0.8.3 Convert to non caching queryset
 | 
			
		||||
        """
 | 
			
		||||
        if self._result_cache is not None:
 | 
			
		||||
            raise OperationError('QuerySet already cached')
 | 
			
		||||
            raise OperationError("QuerySet already cached")
 | 
			
		||||
 | 
			
		||||
        return self._clone_into(QuerySetNoCache(self._document,
 | 
			
		||||
                                                self._collection))
 | 
			
		||||
        return self._clone_into(QuerySetNoCache(self._document, self._collection))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class QuerySetNoCache(BaseQuerySet):
 | 
			
		||||
@@ -165,7 +177,7 @@ class QuerySetNoCache(BaseQuerySet):
 | 
			
		||||
        .. versionchanged:: 0.6.13 Now doesnt modify the cursor
 | 
			
		||||
        """
 | 
			
		||||
        if self._iter:
 | 
			
		||||
            return '.. queryset mid-iteration ..'
 | 
			
		||||
            return ".. queryset mid-iteration .."
 | 
			
		||||
 | 
			
		||||
        data = []
 | 
			
		||||
        for _ in six.moves.range(REPR_OUTPUT_SIZE + 1):
 | 
			
		||||
@@ -175,7 +187,7 @@ class QuerySetNoCache(BaseQuerySet):
 | 
			
		||||
                break
 | 
			
		||||
 | 
			
		||||
        if len(data) > REPR_OUTPUT_SIZE:
 | 
			
		||||
            data[-1] = '...(remaining elements truncated)...'
 | 
			
		||||
            data[-1] = "...(remaining elements truncated)..."
 | 
			
		||||
 | 
			
		||||
        self.rewind()
 | 
			
		||||
        return repr(data)
 | 
			
		||||
 
 | 
			
		||||
@@ -10,21 +10,54 @@ from mongoengine.base import UPDATE_OPERATORS
 | 
			
		||||
from mongoengine.common import _import_class
 | 
			
		||||
from mongoengine.errors import InvalidQueryError
 | 
			
		||||
 | 
			
		||||
__all__ = ('query', 'update')
 | 
			
		||||
__all__ = ("query", "update")
 | 
			
		||||
 | 
			
		||||
COMPARISON_OPERATORS = ('ne', 'gt', 'gte', 'lt', 'lte', 'in', 'nin', 'mod',
 | 
			
		||||
                        'all', 'size', 'exists', 'not', 'elemMatch', 'type')
 | 
			
		||||
GEO_OPERATORS = ('within_distance', 'within_spherical_distance',
 | 
			
		||||
                 'within_box', 'within_polygon', 'near', 'near_sphere',
 | 
			
		||||
                 'max_distance', 'min_distance', 'geo_within', 'geo_within_box',
 | 
			
		||||
                 'geo_within_polygon', 'geo_within_center',
 | 
			
		||||
                 'geo_within_sphere', 'geo_intersects')
 | 
			
		||||
STRING_OPERATORS = ('contains', 'icontains', 'startswith',
 | 
			
		||||
                    'istartswith', 'endswith', 'iendswith',
 | 
			
		||||
                    'exact', 'iexact')
 | 
			
		||||
CUSTOM_OPERATORS = ('match',)
 | 
			
		||||
MATCH_OPERATORS = (COMPARISON_OPERATORS + GEO_OPERATORS +
 | 
			
		||||
                   STRING_OPERATORS + CUSTOM_OPERATORS)
 | 
			
		||||
COMPARISON_OPERATORS = (
 | 
			
		||||
    "ne",
 | 
			
		||||
    "gt",
 | 
			
		||||
    "gte",
 | 
			
		||||
    "lt",
 | 
			
		||||
    "lte",
 | 
			
		||||
    "in",
 | 
			
		||||
    "nin",
 | 
			
		||||
    "mod",
 | 
			
		||||
    "all",
 | 
			
		||||
    "size",
 | 
			
		||||
    "exists",
 | 
			
		||||
    "not",
 | 
			
		||||
    "elemMatch",
 | 
			
		||||
    "type",
 | 
			
		||||
)
 | 
			
		||||
GEO_OPERATORS = (
 | 
			
		||||
    "within_distance",
 | 
			
		||||
    "within_spherical_distance",
 | 
			
		||||
    "within_box",
 | 
			
		||||
    "within_polygon",
 | 
			
		||||
    "near",
 | 
			
		||||
    "near_sphere",
 | 
			
		||||
    "max_distance",
 | 
			
		||||
    "min_distance",
 | 
			
		||||
    "geo_within",
 | 
			
		||||
    "geo_within_box",
 | 
			
		||||
    "geo_within_polygon",
 | 
			
		||||
    "geo_within_center",
 | 
			
		||||
    "geo_within_sphere",
 | 
			
		||||
    "geo_intersects",
 | 
			
		||||
)
 | 
			
		||||
STRING_OPERATORS = (
 | 
			
		||||
    "contains",
 | 
			
		||||
    "icontains",
 | 
			
		||||
    "startswith",
 | 
			
		||||
    "istartswith",
 | 
			
		||||
    "endswith",
 | 
			
		||||
    "iendswith",
 | 
			
		||||
    "exact",
 | 
			
		||||
    "iexact",
 | 
			
		||||
)
 | 
			
		||||
CUSTOM_OPERATORS = ("match",)
 | 
			
		||||
MATCH_OPERATORS = (
 | 
			
		||||
    COMPARISON_OPERATORS + GEO_OPERATORS + STRING_OPERATORS + CUSTOM_OPERATORS
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# TODO make this less complex
 | 
			
		||||
@@ -33,11 +66,11 @@ def query(_doc_cls=None, **kwargs):
 | 
			
		||||
    mongo_query = {}
 | 
			
		||||
    merge_query = defaultdict(list)
 | 
			
		||||
    for key, value in sorted(kwargs.items()):
 | 
			
		||||
        if key == '__raw__':
 | 
			
		||||
        if key == "__raw__":
 | 
			
		||||
            mongo_query.update(value)
 | 
			
		||||
            continue
 | 
			
		||||
 | 
			
		||||
        parts = key.rsplit('__')
 | 
			
		||||
        parts = key.rsplit("__")
 | 
			
		||||
        indices = [(i, p) for i, p in enumerate(parts) if p.isdigit()]
 | 
			
		||||
        parts = [part for part in parts if not part.isdigit()]
 | 
			
		||||
        # Check for an operator and transform to mongo-style if there is
 | 
			
		||||
@@ -46,11 +79,11 @@ def query(_doc_cls=None, **kwargs):
 | 
			
		||||
            op = parts.pop()
 | 
			
		||||
 | 
			
		||||
        # Allow to escape operator-like field name by __
 | 
			
		||||
        if len(parts) > 1 and parts[-1] == '':
 | 
			
		||||
        if len(parts) > 1 and parts[-1] == "":
 | 
			
		||||
            parts.pop()
 | 
			
		||||
 | 
			
		||||
        negate = False
 | 
			
		||||
        if len(parts) > 1 and parts[-1] == 'not':
 | 
			
		||||
        if len(parts) > 1 and parts[-1] == "not":
 | 
			
		||||
            parts.pop()
 | 
			
		||||
            negate = True
 | 
			
		||||
 | 
			
		||||
@@ -62,8 +95,8 @@ def query(_doc_cls=None, **kwargs):
 | 
			
		||||
                raise InvalidQueryError(e)
 | 
			
		||||
            parts = []
 | 
			
		||||
 | 
			
		||||
            CachedReferenceField = _import_class('CachedReferenceField')
 | 
			
		||||
            GenericReferenceField = _import_class('GenericReferenceField')
 | 
			
		||||
            CachedReferenceField = _import_class("CachedReferenceField")
 | 
			
		||||
            GenericReferenceField = _import_class("GenericReferenceField")
 | 
			
		||||
 | 
			
		||||
            cleaned_fields = []
 | 
			
		||||
            for field in fields:
 | 
			
		||||
@@ -73,7 +106,7 @@ def query(_doc_cls=None, **kwargs):
 | 
			
		||||
                    append_field = False
 | 
			
		||||
                # is last and CachedReferenceField
 | 
			
		||||
                elif isinstance(field, CachedReferenceField) and fields[-1] == field:
 | 
			
		||||
                    parts.append('%s._id' % field.db_field)
 | 
			
		||||
                    parts.append("%s._id" % field.db_field)
 | 
			
		||||
                else:
 | 
			
		||||
                    parts.append(field.db_field)
 | 
			
		||||
 | 
			
		||||
@@ -83,15 +116,15 @@ def query(_doc_cls=None, **kwargs):
 | 
			
		||||
            # Convert value to proper value
 | 
			
		||||
            field = cleaned_fields[-1]
 | 
			
		||||
 | 
			
		||||
            singular_ops = [None, 'ne', 'gt', 'gte', 'lt', 'lte', 'not']
 | 
			
		||||
            singular_ops = [None, "ne", "gt", "gte", "lt", "lte", "not"]
 | 
			
		||||
            singular_ops += STRING_OPERATORS
 | 
			
		||||
            if op in singular_ops:
 | 
			
		||||
                value = field.prepare_query_value(op, value)
 | 
			
		||||
 | 
			
		||||
                if isinstance(field, CachedReferenceField) and value:
 | 
			
		||||
                    value = value['_id']
 | 
			
		||||
                    value = value["_id"]
 | 
			
		||||
 | 
			
		||||
            elif op in ('in', 'nin', 'all', 'near') and not isinstance(value, dict):
 | 
			
		||||
            elif op in ("in", "nin", "all", "near") and not isinstance(value, dict):
 | 
			
		||||
                # Raise an error if the in/nin/all/near param is not iterable.
 | 
			
		||||
                value = _prepare_query_for_iterable(field, op, value)
 | 
			
		||||
 | 
			
		||||
@@ -101,39 +134,40 @@ def query(_doc_cls=None, **kwargs):
 | 
			
		||||
            # * If the value is an ObjectId, the key should be "field_name._ref.$id".
 | 
			
		||||
            if isinstance(field, GenericReferenceField):
 | 
			
		||||
                if isinstance(value, DBRef):
 | 
			
		||||
                    parts[-1] += '._ref'
 | 
			
		||||
                    parts[-1] += "._ref"
 | 
			
		||||
                elif isinstance(value, ObjectId):
 | 
			
		||||
                    parts[-1] += '._ref.$id'
 | 
			
		||||
                    parts[-1] += "._ref.$id"
 | 
			
		||||
 | 
			
		||||
        # if op and op not in COMPARISON_OPERATORS:
 | 
			
		||||
        if op:
 | 
			
		||||
            if op in GEO_OPERATORS:
 | 
			
		||||
                value = _geo_operator(field, op, value)
 | 
			
		||||
            elif op in ('match', 'elemMatch'):
 | 
			
		||||
                ListField = _import_class('ListField')
 | 
			
		||||
                EmbeddedDocumentField = _import_class('EmbeddedDocumentField')
 | 
			
		||||
            elif op in ("match", "elemMatch"):
 | 
			
		||||
                ListField = _import_class("ListField")
 | 
			
		||||
                EmbeddedDocumentField = _import_class("EmbeddedDocumentField")
 | 
			
		||||
                if (
 | 
			
		||||
                    isinstance(value, dict) and
 | 
			
		||||
                    isinstance(field, ListField) and
 | 
			
		||||
                    isinstance(field.field, EmbeddedDocumentField)
 | 
			
		||||
                    isinstance(value, dict)
 | 
			
		||||
                    and isinstance(field, ListField)
 | 
			
		||||
                    and isinstance(field.field, EmbeddedDocumentField)
 | 
			
		||||
                ):
 | 
			
		||||
                    value = query(field.field.document_type, **value)
 | 
			
		||||
                else:
 | 
			
		||||
                    value = field.prepare_query_value(op, value)
 | 
			
		||||
                value = {'$elemMatch': value}
 | 
			
		||||
                value = {"$elemMatch": value}
 | 
			
		||||
            elif op in CUSTOM_OPERATORS:
 | 
			
		||||
                NotImplementedError('Custom method "%s" has not '
 | 
			
		||||
                                    'been implemented' % op)
 | 
			
		||||
                NotImplementedError(
 | 
			
		||||
                    'Custom method "%s" has not ' "been implemented" % op
 | 
			
		||||
                )
 | 
			
		||||
            elif op not in STRING_OPERATORS:
 | 
			
		||||
                value = {'$' + op: value}
 | 
			
		||||
                value = {"$" + op: value}
 | 
			
		||||
 | 
			
		||||
        if negate:
 | 
			
		||||
            value = {'$not': value}
 | 
			
		||||
            value = {"$not": value}
 | 
			
		||||
 | 
			
		||||
        for i, part in indices:
 | 
			
		||||
            parts.insert(i, part)
 | 
			
		||||
 | 
			
		||||
        key = '.'.join(parts)
 | 
			
		||||
        key = ".".join(parts)
 | 
			
		||||
 | 
			
		||||
        if op is None or key not in mongo_query:
 | 
			
		||||
            mongo_query[key] = value
 | 
			
		||||
@@ -142,30 +176,35 @@ def query(_doc_cls=None, **kwargs):
 | 
			
		||||
                mongo_query[key].update(value)
 | 
			
		||||
                # $max/minDistance needs to come last - convert to SON
 | 
			
		||||
                value_dict = mongo_query[key]
 | 
			
		||||
                if ('$maxDistance' in value_dict or '$minDistance' in value_dict) and \
 | 
			
		||||
                        ('$near' in value_dict or '$nearSphere' in value_dict):
 | 
			
		||||
                if ("$maxDistance" in value_dict or "$minDistance" in value_dict) and (
 | 
			
		||||
                    "$near" in value_dict or "$nearSphere" in value_dict
 | 
			
		||||
                ):
 | 
			
		||||
                    value_son = SON()
 | 
			
		||||
                    for k, v in iteritems(value_dict):
 | 
			
		||||
                        if k == '$maxDistance' or k == '$minDistance':
 | 
			
		||||
                        if k == "$maxDistance" or k == "$minDistance":
 | 
			
		||||
                            continue
 | 
			
		||||
                        value_son[k] = v
 | 
			
		||||
                    # Required for MongoDB >= 2.6, may fail when combining
 | 
			
		||||
                    # PyMongo 3+ and MongoDB < 2.6
 | 
			
		||||
                    near_embedded = False
 | 
			
		||||
                    for near_op in ('$near', '$nearSphere'):
 | 
			
		||||
                    for near_op in ("$near", "$nearSphere"):
 | 
			
		||||
                        if isinstance(value_dict.get(near_op), dict):
 | 
			
		||||
                            value_son[near_op] = SON(value_son[near_op])
 | 
			
		||||
                            if '$maxDistance' in value_dict:
 | 
			
		||||
                                value_son[near_op]['$maxDistance'] = value_dict['$maxDistance']
 | 
			
		||||
                            if '$minDistance' in value_dict:
 | 
			
		||||
                                value_son[near_op]['$minDistance'] = value_dict['$minDistance']
 | 
			
		||||
                            if "$maxDistance" in value_dict:
 | 
			
		||||
                                value_son[near_op]["$maxDistance"] = value_dict[
 | 
			
		||||
                                    "$maxDistance"
 | 
			
		||||
                                ]
 | 
			
		||||
                            if "$minDistance" in value_dict:
 | 
			
		||||
                                value_son[near_op]["$minDistance"] = value_dict[
 | 
			
		||||
                                    "$minDistance"
 | 
			
		||||
                                ]
 | 
			
		||||
                            near_embedded = True
 | 
			
		||||
 | 
			
		||||
                    if not near_embedded:
 | 
			
		||||
                        if '$maxDistance' in value_dict:
 | 
			
		||||
                            value_son['$maxDistance'] = value_dict['$maxDistance']
 | 
			
		||||
                        if '$minDistance' in value_dict:
 | 
			
		||||
                            value_son['$minDistance'] = value_dict['$minDistance']
 | 
			
		||||
                        if "$maxDistance" in value_dict:
 | 
			
		||||
                            value_son["$maxDistance"] = value_dict["$maxDistance"]
 | 
			
		||||
                        if "$minDistance" in value_dict:
 | 
			
		||||
                            value_son["$minDistance"] = value_dict["$minDistance"]
 | 
			
		||||
                    mongo_query[key] = value_son
 | 
			
		||||
            else:
 | 
			
		||||
                # Store for manually merging later
 | 
			
		||||
@@ -177,10 +216,10 @@ def query(_doc_cls=None, **kwargs):
 | 
			
		||||
        del mongo_query[k]
 | 
			
		||||
        if isinstance(v, list):
 | 
			
		||||
            value = [{k: val} for val in v]
 | 
			
		||||
            if '$and' in mongo_query.keys():
 | 
			
		||||
                mongo_query['$and'].extend(value)
 | 
			
		||||
            if "$and" in mongo_query.keys():
 | 
			
		||||
                mongo_query["$and"].extend(value)
 | 
			
		||||
            else:
 | 
			
		||||
                mongo_query['$and'] = value
 | 
			
		||||
                mongo_query["$and"] = value
 | 
			
		||||
 | 
			
		||||
    return mongo_query
 | 
			
		||||
 | 
			
		||||
@@ -192,15 +231,15 @@ def update(_doc_cls=None, **update):
 | 
			
		||||
    mongo_update = {}
 | 
			
		||||
 | 
			
		||||
    for key, value in update.items():
 | 
			
		||||
        if key == '__raw__':
 | 
			
		||||
        if key == "__raw__":
 | 
			
		||||
            mongo_update.update(value)
 | 
			
		||||
            continue
 | 
			
		||||
 | 
			
		||||
        parts = key.split('__')
 | 
			
		||||
        parts = key.split("__")
 | 
			
		||||
 | 
			
		||||
        # if there is no operator, default to 'set'
 | 
			
		||||
        if len(parts) < 3 and parts[0] not in UPDATE_OPERATORS:
 | 
			
		||||
            parts.insert(0, 'set')
 | 
			
		||||
            parts.insert(0, "set")
 | 
			
		||||
 | 
			
		||||
        # Check for an operator and transform to mongo-style if there is
 | 
			
		||||
        op = None
 | 
			
		||||
@@ -208,13 +247,13 @@ def update(_doc_cls=None, **update):
 | 
			
		||||
            op = parts.pop(0)
 | 
			
		||||
            # Convert Pythonic names to Mongo equivalents
 | 
			
		||||
            operator_map = {
 | 
			
		||||
                'push_all': 'pushAll',
 | 
			
		||||
                'pull_all': 'pullAll',
 | 
			
		||||
                'dec': 'inc',
 | 
			
		||||
                'add_to_set': 'addToSet',
 | 
			
		||||
                'set_on_insert': 'setOnInsert'
 | 
			
		||||
                "push_all": "pushAll",
 | 
			
		||||
                "pull_all": "pullAll",
 | 
			
		||||
                "dec": "inc",
 | 
			
		||||
                "add_to_set": "addToSet",
 | 
			
		||||
                "set_on_insert": "setOnInsert",
 | 
			
		||||
            }
 | 
			
		||||
            if op == 'dec':
 | 
			
		||||
            if op == "dec":
 | 
			
		||||
                # Support decrement by flipping a positive value's sign
 | 
			
		||||
                # and using 'inc'
 | 
			
		||||
                value = -value
 | 
			
		||||
@@ -227,7 +266,7 @@ def update(_doc_cls=None, **update):
 | 
			
		||||
            match = parts.pop()
 | 
			
		||||
 | 
			
		||||
        # Allow to escape operator-like field name by __
 | 
			
		||||
        if len(parts) > 1 and parts[-1] == '':
 | 
			
		||||
        if len(parts) > 1 and parts[-1] == "":
 | 
			
		||||
            parts.pop()
 | 
			
		||||
 | 
			
		||||
        if _doc_cls:
 | 
			
		||||
@@ -244,8 +283,8 @@ def update(_doc_cls=None, **update):
 | 
			
		||||
                append_field = True
 | 
			
		||||
                if isinstance(field, six.string_types):
 | 
			
		||||
                    # Convert the S operator to $
 | 
			
		||||
                    if field == 'S':
 | 
			
		||||
                        field = '$'
 | 
			
		||||
                    if field == "S":
 | 
			
		||||
                        field = "$"
 | 
			
		||||
                    parts.append(field)
 | 
			
		||||
                    append_field = False
 | 
			
		||||
                else:
 | 
			
		||||
@@ -253,7 +292,7 @@ def update(_doc_cls=None, **update):
 | 
			
		||||
                if append_field:
 | 
			
		||||
                    appended_sub_field = False
 | 
			
		||||
                    cleaned_fields.append(field)
 | 
			
		||||
                    if hasattr(field, 'field'):
 | 
			
		||||
                    if hasattr(field, "field"):
 | 
			
		||||
                        cleaned_fields.append(field.field)
 | 
			
		||||
                        appended_sub_field = True
 | 
			
		||||
 | 
			
		||||
@@ -263,52 +302,53 @@ def update(_doc_cls=None, **update):
 | 
			
		||||
            else:
 | 
			
		||||
                field = cleaned_fields[-1]
 | 
			
		||||
 | 
			
		||||
            GeoJsonBaseField = _import_class('GeoJsonBaseField')
 | 
			
		||||
            GeoJsonBaseField = _import_class("GeoJsonBaseField")
 | 
			
		||||
            if isinstance(field, GeoJsonBaseField):
 | 
			
		||||
                value = field.to_mongo(value)
 | 
			
		||||
 | 
			
		||||
            if op == 'pull':
 | 
			
		||||
            if op == "pull":
 | 
			
		||||
                if field.required or value is not None:
 | 
			
		||||
                    if match in ('in', 'nin') and not isinstance(value, dict):
 | 
			
		||||
                    if match in ("in", "nin") and not isinstance(value, dict):
 | 
			
		||||
                        value = _prepare_query_for_iterable(field, op, value)
 | 
			
		||||
                    else:
 | 
			
		||||
                        value = field.prepare_query_value(op, value)
 | 
			
		||||
            elif op == 'push' and isinstance(value, (list, tuple, set)):
 | 
			
		||||
            elif op == "push" and isinstance(value, (list, tuple, set)):
 | 
			
		||||
                value = [field.prepare_query_value(op, v) for v in value]
 | 
			
		||||
            elif op in (None, 'set', 'push'):
 | 
			
		||||
            elif op in (None, "set", "push"):
 | 
			
		||||
                if field.required or value is not None:
 | 
			
		||||
                    value = field.prepare_query_value(op, value)
 | 
			
		||||
            elif op in ('pushAll', 'pullAll'):
 | 
			
		||||
            elif op in ("pushAll", "pullAll"):
 | 
			
		||||
                value = [field.prepare_query_value(op, v) for v in value]
 | 
			
		||||
            elif op in ('addToSet', 'setOnInsert'):
 | 
			
		||||
            elif op in ("addToSet", "setOnInsert"):
 | 
			
		||||
                if isinstance(value, (list, tuple, set)):
 | 
			
		||||
                    value = [field.prepare_query_value(op, v) for v in value]
 | 
			
		||||
                elif field.required or value is not None:
 | 
			
		||||
                    value = field.prepare_query_value(op, value)
 | 
			
		||||
            elif op == 'unset':
 | 
			
		||||
            elif op == "unset":
 | 
			
		||||
                value = 1
 | 
			
		||||
            elif op == 'inc':
 | 
			
		||||
            elif op == "inc":
 | 
			
		||||
                value = field.prepare_query_value(op, value)
 | 
			
		||||
 | 
			
		||||
        if match:
 | 
			
		||||
            match = '$' + match
 | 
			
		||||
            match = "$" + match
 | 
			
		||||
            value = {match: value}
 | 
			
		||||
 | 
			
		||||
        key = '.'.join(parts)
 | 
			
		||||
        key = ".".join(parts)
 | 
			
		||||
 | 
			
		||||
        if 'pull' in op and '.' in key:
 | 
			
		||||
        if "pull" in op and "." in key:
 | 
			
		||||
            # Dot operators don't work on pull operations
 | 
			
		||||
            # unless they point to a list field
 | 
			
		||||
            # Otherwise it uses nested dict syntax
 | 
			
		||||
            if op == 'pullAll':
 | 
			
		||||
                raise InvalidQueryError('pullAll operations only support '
 | 
			
		||||
                                        'a single field depth')
 | 
			
		||||
            if op == "pullAll":
 | 
			
		||||
                raise InvalidQueryError(
 | 
			
		||||
                    "pullAll operations only support a single field depth"
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
            # Look for the last list field and use dot notation until there
 | 
			
		||||
            field_classes = [c.__class__ for c in cleaned_fields]
 | 
			
		||||
            field_classes.reverse()
 | 
			
		||||
            ListField = _import_class('ListField')
 | 
			
		||||
            EmbeddedDocumentListField = _import_class('EmbeddedDocumentListField')
 | 
			
		||||
            ListField = _import_class("ListField")
 | 
			
		||||
            EmbeddedDocumentListField = _import_class("EmbeddedDocumentListField")
 | 
			
		||||
            if ListField in field_classes or EmbeddedDocumentListField in field_classes:
 | 
			
		||||
                # Join all fields via dot notation to the last ListField or EmbeddedDocumentListField
 | 
			
		||||
                # Then process as normal
 | 
			
		||||
@@ -317,37 +357,36 @@ def update(_doc_cls=None, **update):
 | 
			
		||||
                else:
 | 
			
		||||
                    _check_field = EmbeddedDocumentListField
 | 
			
		||||
 | 
			
		||||
                last_listField = len(
 | 
			
		||||
                    cleaned_fields) - field_classes.index(_check_field)
 | 
			
		||||
                key = '.'.join(parts[:last_listField])
 | 
			
		||||
                last_listField = len(cleaned_fields) - field_classes.index(_check_field)
 | 
			
		||||
                key = ".".join(parts[:last_listField])
 | 
			
		||||
                parts = parts[last_listField:]
 | 
			
		||||
                parts.insert(0, key)
 | 
			
		||||
 | 
			
		||||
            parts.reverse()
 | 
			
		||||
            for key in parts:
 | 
			
		||||
                value = {key: value}
 | 
			
		||||
        elif op == 'addToSet' and isinstance(value, list):
 | 
			
		||||
            value = {key: {'$each': value}}
 | 
			
		||||
        elif op in ('push', 'pushAll'):
 | 
			
		||||
        elif op == "addToSet" and isinstance(value, list):
 | 
			
		||||
            value = {key: {"$each": value}}
 | 
			
		||||
        elif op in ("push", "pushAll"):
 | 
			
		||||
            if parts[-1].isdigit():
 | 
			
		||||
                key = '.'.join(parts[0:-1])
 | 
			
		||||
                key = ".".join(parts[0:-1])
 | 
			
		||||
                position = int(parts[-1])
 | 
			
		||||
                # $position expects an iterable. If pushing a single value,
 | 
			
		||||
                # wrap it in a list.
 | 
			
		||||
                if not isinstance(value, (set, tuple, list)):
 | 
			
		||||
                    value = [value]
 | 
			
		||||
                value = {key: {'$each': value, '$position': position}}
 | 
			
		||||
                value = {key: {"$each": value, "$position": position}}
 | 
			
		||||
            else:
 | 
			
		||||
                if op == 'pushAll':
 | 
			
		||||
                    op = 'push'  # convert to non-deprecated keyword
 | 
			
		||||
                if op == "pushAll":
 | 
			
		||||
                    op = "push"  # convert to non-deprecated keyword
 | 
			
		||||
                    if not isinstance(value, (set, tuple, list)):
 | 
			
		||||
                        value = [value]
 | 
			
		||||
                    value = {key: {'$each': value}}
 | 
			
		||||
                    value = {key: {"$each": value}}
 | 
			
		||||
                else:
 | 
			
		||||
                    value = {key: value}
 | 
			
		||||
        else:
 | 
			
		||||
            value = {key: value}
 | 
			
		||||
        key = '$' + op
 | 
			
		||||
        key = "$" + op
 | 
			
		||||
        if key not in mongo_update:
 | 
			
		||||
            mongo_update[key] = value
 | 
			
		||||
        elif key in mongo_update and isinstance(mongo_update[key], dict):
 | 
			
		||||
@@ -358,45 +397,45 @@ def update(_doc_cls=None, **update):
 | 
			
		||||
 | 
			
		||||
def _geo_operator(field, op, value):
 | 
			
		||||
    """Helper to return the query for a given geo query."""
 | 
			
		||||
    if op == 'max_distance':
 | 
			
		||||
        value = {'$maxDistance': value}
 | 
			
		||||
    elif op == 'min_distance':
 | 
			
		||||
        value = {'$minDistance': value}
 | 
			
		||||
    if op == "max_distance":
 | 
			
		||||
        value = {"$maxDistance": value}
 | 
			
		||||
    elif op == "min_distance":
 | 
			
		||||
        value = {"$minDistance": value}
 | 
			
		||||
    elif field._geo_index == pymongo.GEO2D:
 | 
			
		||||
        if op == 'within_distance':
 | 
			
		||||
            value = {'$within': {'$center': value}}
 | 
			
		||||
        elif op == 'within_spherical_distance':
 | 
			
		||||
            value = {'$within': {'$centerSphere': value}}
 | 
			
		||||
        elif op == 'within_polygon':
 | 
			
		||||
            value = {'$within': {'$polygon': value}}
 | 
			
		||||
        elif op == 'near':
 | 
			
		||||
            value = {'$near': value}
 | 
			
		||||
        elif op == 'near_sphere':
 | 
			
		||||
            value = {'$nearSphere': value}
 | 
			
		||||
        elif op == 'within_box':
 | 
			
		||||
            value = {'$within': {'$box': value}}
 | 
			
		||||
        else:
 | 
			
		||||
            raise NotImplementedError('Geo method "%s" has not been '
 | 
			
		||||
                                      'implemented for a GeoPointField' % op)
 | 
			
		||||
    else:
 | 
			
		||||
        if op == 'geo_within':
 | 
			
		||||
            value = {'$geoWithin': _infer_geometry(value)}
 | 
			
		||||
        elif op == 'geo_within_box':
 | 
			
		||||
            value = {'$geoWithin': {'$box': value}}
 | 
			
		||||
        elif op == 'geo_within_polygon':
 | 
			
		||||
            value = {'$geoWithin': {'$polygon': value}}
 | 
			
		||||
        elif op == 'geo_within_center':
 | 
			
		||||
            value = {'$geoWithin': {'$center': value}}
 | 
			
		||||
        elif op == 'geo_within_sphere':
 | 
			
		||||
            value = {'$geoWithin': {'$centerSphere': value}}
 | 
			
		||||
        elif op == 'geo_intersects':
 | 
			
		||||
            value = {'$geoIntersects': _infer_geometry(value)}
 | 
			
		||||
        elif op == 'near':
 | 
			
		||||
            value = {'$near': _infer_geometry(value)}
 | 
			
		||||
        if op == "within_distance":
 | 
			
		||||
            value = {"$within": {"$center": value}}
 | 
			
		||||
        elif op == "within_spherical_distance":
 | 
			
		||||
            value = {"$within": {"$centerSphere": value}}
 | 
			
		||||
        elif op == "within_polygon":
 | 
			
		||||
            value = {"$within": {"$polygon": value}}
 | 
			
		||||
        elif op == "near":
 | 
			
		||||
            value = {"$near": value}
 | 
			
		||||
        elif op == "near_sphere":
 | 
			
		||||
            value = {"$nearSphere": value}
 | 
			
		||||
        elif op == "within_box":
 | 
			
		||||
            value = {"$within": {"$box": value}}
 | 
			
		||||
        else:
 | 
			
		||||
            raise NotImplementedError(
 | 
			
		||||
                'Geo method "%s" has not been implemented for a %s '
 | 
			
		||||
                % (op, field._name)
 | 
			
		||||
                'Geo method "%s" has not been ' "implemented for a GeoPointField" % op
 | 
			
		||||
            )
 | 
			
		||||
    else:
 | 
			
		||||
        if op == "geo_within":
 | 
			
		||||
            value = {"$geoWithin": _infer_geometry(value)}
 | 
			
		||||
        elif op == "geo_within_box":
 | 
			
		||||
            value = {"$geoWithin": {"$box": value}}
 | 
			
		||||
        elif op == "geo_within_polygon":
 | 
			
		||||
            value = {"$geoWithin": {"$polygon": value}}
 | 
			
		||||
        elif op == "geo_within_center":
 | 
			
		||||
            value = {"$geoWithin": {"$center": value}}
 | 
			
		||||
        elif op == "geo_within_sphere":
 | 
			
		||||
            value = {"$geoWithin": {"$centerSphere": value}}
 | 
			
		||||
        elif op == "geo_intersects":
 | 
			
		||||
            value = {"$geoIntersects": _infer_geometry(value)}
 | 
			
		||||
        elif op == "near":
 | 
			
		||||
            value = {"$near": _infer_geometry(value)}
 | 
			
		||||
        else:
 | 
			
		||||
            raise NotImplementedError(
 | 
			
		||||
                'Geo method "%s" has not been implemented for a %s ' % (op, field._name)
 | 
			
		||||
            )
 | 
			
		||||
    return value
 | 
			
		||||
 | 
			
		||||
@@ -406,51 +445,58 @@ def _infer_geometry(value):
 | 
			
		||||
    given value.
 | 
			
		||||
    """
 | 
			
		||||
    if isinstance(value, dict):
 | 
			
		||||
        if '$geometry' in value:
 | 
			
		||||
        if "$geometry" in value:
 | 
			
		||||
            return value
 | 
			
		||||
        elif 'coordinates' in value and 'type' in value:
 | 
			
		||||
            return {'$geometry': value}
 | 
			
		||||
        raise InvalidQueryError('Invalid $geometry dictionary should have '
 | 
			
		||||
                                'type and coordinates keys')
 | 
			
		||||
        elif "coordinates" in value and "type" in value:
 | 
			
		||||
            return {"$geometry": value}
 | 
			
		||||
        raise InvalidQueryError(
 | 
			
		||||
            "Invalid $geometry dictionary should have type and coordinates keys"
 | 
			
		||||
        )
 | 
			
		||||
    elif isinstance(value, (list, set)):
 | 
			
		||||
        # TODO: shouldn't we test value[0][0][0][0] to see if it is MultiPolygon?
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            value[0][0][0]
 | 
			
		||||
            return {'$geometry': {'type': 'Polygon', 'coordinates': value}}
 | 
			
		||||
            return {"$geometry": {"type": "Polygon", "coordinates": value}}
 | 
			
		||||
        except (TypeError, IndexError):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            value[0][0]
 | 
			
		||||
            return {'$geometry': {'type': 'LineString', 'coordinates': value}}
 | 
			
		||||
            return {"$geometry": {"type": "LineString", "coordinates": value}}
 | 
			
		||||
        except (TypeError, IndexError):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            value[0]
 | 
			
		||||
            return {'$geometry': {'type': 'Point', 'coordinates': value}}
 | 
			
		||||
            return {"$geometry": {"type": "Point", "coordinates": value}}
 | 
			
		||||
        except (TypeError, IndexError):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
    raise InvalidQueryError('Invalid $geometry data. Can be either a '
 | 
			
		||||
                            'dictionary or (nested) lists of coordinate(s)')
 | 
			
		||||
    raise InvalidQueryError(
 | 
			
		||||
        "Invalid $geometry data. Can be either a "
 | 
			
		||||
        "dictionary or (nested) lists of coordinate(s)"
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _prepare_query_for_iterable(field, op, value):
 | 
			
		||||
    # We need a special check for BaseDocument, because - although it's iterable - using
 | 
			
		||||
    # it as such in the context of this method is most definitely a mistake.
 | 
			
		||||
    BaseDocument = _import_class('BaseDocument')
 | 
			
		||||
    BaseDocument = _import_class("BaseDocument")
 | 
			
		||||
 | 
			
		||||
    if isinstance(value, BaseDocument):
 | 
			
		||||
        raise TypeError("When using the `in`, `nin`, `all`, or "
 | 
			
		||||
                        "`near`-operators you can\'t use a "
 | 
			
		||||
                        "`Document`, you must wrap your object "
 | 
			
		||||
                        "in a list (object -> [object]).")
 | 
			
		||||
        raise TypeError(
 | 
			
		||||
            "When using the `in`, `nin`, `all`, or "
 | 
			
		||||
            "`near`-operators you can't use a "
 | 
			
		||||
            "`Document`, you must wrap your object "
 | 
			
		||||
            "in a list (object -> [object])."
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    if not hasattr(value, '__iter__'):
 | 
			
		||||
        raise TypeError("The `in`, `nin`, `all`, or "
 | 
			
		||||
                        "`near`-operators must be applied to an "
 | 
			
		||||
                        "iterable (e.g. a list).")
 | 
			
		||||
    if not hasattr(value, "__iter__"):
 | 
			
		||||
        raise TypeError(
 | 
			
		||||
            "The `in`, `nin`, `all`, or "
 | 
			
		||||
            "`near`-operators must be applied to an "
 | 
			
		||||
            "iterable (e.g. a list)."
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    return [field.prepare_query_value(op, v) for v in value]
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@ import copy
 | 
			
		||||
from mongoengine.errors import InvalidQueryError
 | 
			
		||||
from mongoengine.queryset import transform
 | 
			
		||||
 | 
			
		||||
__all__ = ('Q', 'QNode')
 | 
			
		||||
__all__ = ("Q", "QNode")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class QNodeVisitor(object):
 | 
			
		||||
@@ -69,9 +69,9 @@ class QueryCompilerVisitor(QNodeVisitor):
 | 
			
		||||
        self.document = document
 | 
			
		||||
 | 
			
		||||
    def visit_combination(self, combination):
 | 
			
		||||
        operator = '$and'
 | 
			
		||||
        operator = "$and"
 | 
			
		||||
        if combination.operation == combination.OR:
 | 
			
		||||
            operator = '$or'
 | 
			
		||||
            operator = "$or"
 | 
			
		||||
        return {operator: combination.children}
 | 
			
		||||
 | 
			
		||||
    def visit_query(self, query):
 | 
			
		||||
@@ -96,7 +96,7 @@ class QNode(object):
 | 
			
		||||
        """Combine this node with another node into a QCombination
 | 
			
		||||
        object.
 | 
			
		||||
        """
 | 
			
		||||
        if getattr(other, 'empty', True):
 | 
			
		||||
        if getattr(other, "empty", True):
 | 
			
		||||
            return self
 | 
			
		||||
 | 
			
		||||
        if self.empty:
 | 
			
		||||
@@ -132,8 +132,8 @@ class QCombination(QNode):
 | 
			
		||||
                self.children.append(node)
 | 
			
		||||
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
        op = ' & ' if self.operation is self.AND else ' | '
 | 
			
		||||
        return '(%s)' % op.join([repr(node) for node in self.children])
 | 
			
		||||
        op = " & " if self.operation is self.AND else " | "
 | 
			
		||||
        return "(%s)" % op.join([repr(node) for node in self.children])
 | 
			
		||||
 | 
			
		||||
    def accept(self, visitor):
 | 
			
		||||
        for i in range(len(self.children)):
 | 
			
		||||
@@ -156,7 +156,7 @@ class Q(QNode):
 | 
			
		||||
        self.query = query
 | 
			
		||||
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
        return 'Q(**%s)' % repr(self.query)
 | 
			
		||||
        return "Q(**%s)" % repr(self.query)
 | 
			
		||||
 | 
			
		||||
    def accept(self, visitor):
 | 
			
		||||
        return visitor.visit_query(self)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,12 @@
 | 
			
		||||
__all__ = ('pre_init', 'post_init', 'pre_save', 'pre_save_post_validation',
 | 
			
		||||
           'post_save', 'pre_delete', 'post_delete')
 | 
			
		||||
__all__ = (
 | 
			
		||||
    "pre_init",
 | 
			
		||||
    "post_init",
 | 
			
		||||
    "pre_save",
 | 
			
		||||
    "pre_save_post_validation",
 | 
			
		||||
    "post_save",
 | 
			
		||||
    "pre_delete",
 | 
			
		||||
    "post_delete",
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
signals_available = False
 | 
			
		||||
try:
 | 
			
		||||
@@ -7,6 +14,7 @@ try:
 | 
			
		||||
 | 
			
		||||
    signals_available = True
 | 
			
		||||
except ImportError:
 | 
			
		||||
 | 
			
		||||
    class Namespace(object):
 | 
			
		||||
        def signal(self, name, doc=None):
 | 
			
		||||
            return _FakeSignal(name, doc)
 | 
			
		||||
@@ -23,13 +31,16 @@ except ImportError:
 | 
			
		||||
            self.__doc__ = doc
 | 
			
		||||
 | 
			
		||||
        def _fail(self, *args, **kwargs):
 | 
			
		||||
            raise RuntimeError('signalling support is unavailable '
 | 
			
		||||
                               'because the blinker library is '
 | 
			
		||||
                               'not installed.')
 | 
			
		||||
            raise RuntimeError(
 | 
			
		||||
                "signalling support is unavailable "
 | 
			
		||||
                "because the blinker library is "
 | 
			
		||||
                "not installed."
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        send = lambda *a, **kw: None  # noqa
 | 
			
		||||
        connect = disconnect = has_receivers_for = receivers_for = \
 | 
			
		||||
            temporarily_connected_to = _fail
 | 
			
		||||
        connect = (
 | 
			
		||||
            disconnect
 | 
			
		||||
        ) = has_receivers_for = receivers_for = temporarily_connected_to = _fail
 | 
			
		||||
        del _fail
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -37,12 +48,12 @@ except ImportError:
 | 
			
		||||
# not put signals in here.  Create your own namespace instead.
 | 
			
		||||
_signals = Namespace()
 | 
			
		||||
 | 
			
		||||
pre_init = _signals.signal('pre_init')
 | 
			
		||||
post_init = _signals.signal('post_init')
 | 
			
		||||
pre_save = _signals.signal('pre_save')
 | 
			
		||||
pre_save_post_validation = _signals.signal('pre_save_post_validation')
 | 
			
		||||
post_save = _signals.signal('post_save')
 | 
			
		||||
pre_delete = _signals.signal('pre_delete')
 | 
			
		||||
post_delete = _signals.signal('post_delete')
 | 
			
		||||
pre_bulk_insert = _signals.signal('pre_bulk_insert')
 | 
			
		||||
post_bulk_insert = _signals.signal('post_bulk_insert')
 | 
			
		||||
pre_init = _signals.signal("pre_init")
 | 
			
		||||
post_init = _signals.signal("post_init")
 | 
			
		||||
pre_save = _signals.signal("pre_save")
 | 
			
		||||
pre_save_post_validation = _signals.signal("pre_save_post_validation")
 | 
			
		||||
post_save = _signals.signal("post_save")
 | 
			
		||||
pre_delete = _signals.signal("pre_delete")
 | 
			
		||||
post_delete = _signals.signal("post_delete")
 | 
			
		||||
pre_bulk_insert = _signals.signal("pre_bulk_insert")
 | 
			
		||||
post_bulk_insert = _signals.signal("post_bulk_insert")
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
black
 | 
			
		||||
nose
 | 
			
		||||
pymongo>=3.4
 | 
			
		||||
six==1.10.0
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ detailed-errors=1
 | 
			
		||||
cover-package=mongoengine
 | 
			
		||||
 | 
			
		||||
[flake8]
 | 
			
		||||
ignore=E501,F401,F403,F405,I201,I202,W504, W605
 | 
			
		||||
ignore=E501,F401,F403,F405,I201,I202,W504, W605, W503
 | 
			
		||||
exclude=build,dist,docs,venv,venv3,.tox,.eggs,tests
 | 
			
		||||
max-complexity=47
 | 
			
		||||
application-import-names=mongoengine,tests
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										64
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										64
									
								
								setup.py
									
									
									
									
									
								
							@@ -8,13 +8,10 @@ try:
 | 
			
		||||
except ImportError:
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
DESCRIPTION = (
 | 
			
		||||
    'MongoEngine is a Python Object-Document '
 | 
			
		||||
    'Mapper for working with MongoDB.'
 | 
			
		||||
)
 | 
			
		||||
DESCRIPTION = "MongoEngine is a Python Object-Document Mapper for working with MongoDB."
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    with open('README.rst') as fin:
 | 
			
		||||
    with open("README.rst") as fin:
 | 
			
		||||
        LONG_DESCRIPTION = fin.read()
 | 
			
		||||
except Exception:
 | 
			
		||||
    LONG_DESCRIPTION = None
 | 
			
		||||
@@ -24,23 +21,23 @@ def get_version(version_tuple):
 | 
			
		||||
    """Return the version tuple as a string, e.g. for (0, 10, 7),
 | 
			
		||||
    return '0.10.7'.
 | 
			
		||||
    """
 | 
			
		||||
    return '.'.join(map(str, version_tuple))
 | 
			
		||||
    return ".".join(map(str, version_tuple))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Dirty hack to get version number from monogengine/__init__.py - we can't
 | 
			
		||||
# import it as it depends on PyMongo and PyMongo isn't installed until this
 | 
			
		||||
# file is read
 | 
			
		||||
init = os.path.join(os.path.dirname(__file__), 'mongoengine', '__init__.py')
 | 
			
		||||
version_line = list(filter(lambda l: l.startswith('VERSION'), open(init)))[0]
 | 
			
		||||
init = os.path.join(os.path.dirname(__file__), "mongoengine", "__init__.py")
 | 
			
		||||
version_line = list(filter(lambda l: l.startswith("VERSION"), open(init)))[0]
 | 
			
		||||
 | 
			
		||||
VERSION = get_version(eval(version_line.split('=')[-1]))
 | 
			
		||||
VERSION = get_version(eval(version_line.split("=")[-1]))
 | 
			
		||||
 | 
			
		||||
CLASSIFIERS = [
 | 
			
		||||
    'Development Status :: 4 - Beta',
 | 
			
		||||
    'Intended Audience :: Developers',
 | 
			
		||||
    'License :: OSI Approved :: MIT License',
 | 
			
		||||
    'Operating System :: OS Independent',
 | 
			
		||||
    'Programming Language :: Python',
 | 
			
		||||
    "Development Status :: 4 - Beta",
 | 
			
		||||
    "Intended Audience :: Developers",
 | 
			
		||||
    "License :: OSI Approved :: MIT License",
 | 
			
		||||
    "Operating System :: OS Independent",
 | 
			
		||||
    "Programming Language :: Python",
 | 
			
		||||
    "Programming Language :: Python :: 2",
 | 
			
		||||
    "Programming Language :: Python :: 2.7",
 | 
			
		||||
    "Programming Language :: Python :: 3",
 | 
			
		||||
@@ -48,39 +45,40 @@ CLASSIFIERS = [
 | 
			
		||||
    "Programming Language :: Python :: 3.6",
 | 
			
		||||
    "Programming Language :: Python :: Implementation :: CPython",
 | 
			
		||||
    "Programming Language :: Python :: Implementation :: PyPy",
 | 
			
		||||
    'Topic :: Database',
 | 
			
		||||
    'Topic :: Software Development :: Libraries :: Python Modules',
 | 
			
		||||
    "Topic :: Database",
 | 
			
		||||
    "Topic :: Software Development :: Libraries :: Python Modules",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
extra_opts = {
 | 
			
		||||
    'packages': find_packages(exclude=['tests', 'tests.*']),
 | 
			
		||||
    'tests_require': ['nose', 'coverage==4.2', 'blinker', 'Pillow>=2.0.0']
 | 
			
		||||
    "packages": find_packages(exclude=["tests", "tests.*"]),
 | 
			
		||||
    "tests_require": ["nose", "coverage==4.2", "blinker", "Pillow>=2.0.0"],
 | 
			
		||||
}
 | 
			
		||||
if sys.version_info[0] == 3:
 | 
			
		||||
    extra_opts['use_2to3'] = True
 | 
			
		||||
    if 'test' in sys.argv or 'nosetests' in sys.argv:
 | 
			
		||||
        extra_opts['packages'] = find_packages()
 | 
			
		||||
        extra_opts['package_data'] = {
 | 
			
		||||
            'tests': ['fields/mongoengine.png', 'fields/mongodb_leaf.png']}
 | 
			
		||||
    extra_opts["use_2to3"] = True
 | 
			
		||||
    if "test" in sys.argv or "nosetests" in sys.argv:
 | 
			
		||||
        extra_opts["packages"] = find_packages()
 | 
			
		||||
        extra_opts["package_data"] = {
 | 
			
		||||
            "tests": ["fields/mongoengine.png", "fields/mongodb_leaf.png"]
 | 
			
		||||
        }
 | 
			
		||||
else:
 | 
			
		||||
    extra_opts['tests_require'] += ['python-dateutil']
 | 
			
		||||
    extra_opts["tests_require"] += ["python-dateutil"]
 | 
			
		||||
 | 
			
		||||
setup(
 | 
			
		||||
    name='mongoengine',
 | 
			
		||||
    name="mongoengine",
 | 
			
		||||
    version=VERSION,
 | 
			
		||||
    author='Harry Marr',
 | 
			
		||||
    author_email='harry.marr@gmail.com',
 | 
			
		||||
    author="Harry Marr",
 | 
			
		||||
    author_email="harry.marr@gmail.com",
 | 
			
		||||
    maintainer="Stefan Wojcik",
 | 
			
		||||
    maintainer_email="wojcikstefan@gmail.com",
 | 
			
		||||
    url='http://mongoengine.org/',
 | 
			
		||||
    download_url='https://github.com/MongoEngine/mongoengine/tarball/master',
 | 
			
		||||
    license='MIT',
 | 
			
		||||
    url="http://mongoengine.org/",
 | 
			
		||||
    download_url="https://github.com/MongoEngine/mongoengine/tarball/master",
 | 
			
		||||
    license="MIT",
 | 
			
		||||
    include_package_data=True,
 | 
			
		||||
    description=DESCRIPTION,
 | 
			
		||||
    long_description=LONG_DESCRIPTION,
 | 
			
		||||
    platforms=['any'],
 | 
			
		||||
    platforms=["any"],
 | 
			
		||||
    classifiers=CLASSIFIERS,
 | 
			
		||||
    install_requires=['pymongo>=3.4', 'six'],
 | 
			
		||||
    test_suite='nose.collector',
 | 
			
		||||
    install_requires=["pymongo>=3.4", "six"],
 | 
			
		||||
    test_suite="nose.collector",
 | 
			
		||||
    **extra_opts
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -9,34 +9,32 @@ import warnings
 | 
			
		||||
from mongoengine import *
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ('AllWarnings', )
 | 
			
		||||
__all__ = ("AllWarnings",)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AllWarnings(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        connect(db='mongoenginetest')
 | 
			
		||||
        connect(db="mongoenginetest")
 | 
			
		||||
        self.warning_list = []
 | 
			
		||||
        self.showwarning_default = warnings.showwarning
 | 
			
		||||
        warnings.showwarning = self.append_to_warning_list
 | 
			
		||||
 | 
			
		||||
    def append_to_warning_list(self, message, category, *args):
 | 
			
		||||
        self.warning_list.append({"message": message,
 | 
			
		||||
                                  "category": category})
 | 
			
		||||
        self.warning_list.append({"message": message, "category": category})
 | 
			
		||||
 | 
			
		||||
    def tearDown(self):
 | 
			
		||||
        # restore default handling of warnings
 | 
			
		||||
        warnings.showwarning = self.showwarning_default
 | 
			
		||||
 | 
			
		||||
    def test_document_collection_syntax_warning(self):
 | 
			
		||||
 | 
			
		||||
        class NonAbstractBase(Document):
 | 
			
		||||
            meta = {'allow_inheritance': True}
 | 
			
		||||
            meta = {"allow_inheritance": True}
 | 
			
		||||
 | 
			
		||||
        class InheritedDocumentFailTest(NonAbstractBase):
 | 
			
		||||
            meta = {'collection': 'fail'}
 | 
			
		||||
            meta = {"collection": "fail"}
 | 
			
		||||
 | 
			
		||||
        warning = self.warning_list[0]
 | 
			
		||||
        self.assertEqual(SyntaxWarning, warning["category"])
 | 
			
		||||
        self.assertEqual('non_abstract_base',
 | 
			
		||||
                         InheritedDocumentFailTest._get_collection_name())
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            "non_abstract_base", InheritedDocumentFailTest._get_collection_name()
 | 
			
		||||
        )
 | 
			
		||||
 
 | 
			
		||||
@@ -9,5 +9,5 @@ from .instance import *
 | 
			
		||||
from .json_serialisation import *
 | 
			
		||||
from .validation import *
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    unittest.main()
 | 
			
		||||
 
 | 
			
		||||
@@ -7,13 +7,12 @@ from mongoengine.pymongo_support import list_collection_names
 | 
			
		||||
from mongoengine.queryset import NULLIFY, PULL
 | 
			
		||||
from mongoengine.connection import get_db
 | 
			
		||||
 | 
			
		||||
__all__ = ("ClassMethodsTest", )
 | 
			
		||||
__all__ = ("ClassMethodsTest",)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ClassMethodsTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        connect(db='mongoenginetest')
 | 
			
		||||
        connect(db="mongoenginetest")
 | 
			
		||||
        self.db = get_db()
 | 
			
		||||
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
@@ -33,11 +32,13 @@ class ClassMethodsTest(unittest.TestCase):
 | 
			
		||||
    def test_definition(self):
 | 
			
		||||
        """Ensure that document may be defined using fields.
 | 
			
		||||
        """
 | 
			
		||||
        self.assertEqual(['_cls', 'age', 'id', 'name'],
 | 
			
		||||
                         sorted(self.Person._fields.keys()))
 | 
			
		||||
        self.assertEqual(["IntField", "ObjectIdField", "StringField", "StringField"],
 | 
			
		||||
                        sorted([x.__class__.__name__ for x in
 | 
			
		||||
                                self.Person._fields.values()]))
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            ["_cls", "age", "id", "name"], sorted(self.Person._fields.keys())
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            ["IntField", "ObjectIdField", "StringField", "StringField"],
 | 
			
		||||
            sorted([x.__class__.__name__ for x in self.Person._fields.values()]),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_get_db(self):
 | 
			
		||||
        """Ensure that get_db returns the expected db.
 | 
			
		||||
@@ -49,21 +50,21 @@ class ClassMethodsTest(unittest.TestCase):
 | 
			
		||||
        """Ensure that get_collection_name returns the expected collection
 | 
			
		||||
        name.
 | 
			
		||||
        """
 | 
			
		||||
        collection_name = 'person'
 | 
			
		||||
        collection_name = "person"
 | 
			
		||||
        self.assertEqual(collection_name, self.Person._get_collection_name())
 | 
			
		||||
 | 
			
		||||
    def test_get_collection(self):
 | 
			
		||||
        """Ensure that get_collection returns the expected collection.
 | 
			
		||||
        """
 | 
			
		||||
        collection_name = 'person'
 | 
			
		||||
        collection_name = "person"
 | 
			
		||||
        collection = self.Person._get_collection()
 | 
			
		||||
        self.assertEqual(self.db[collection_name], collection)
 | 
			
		||||
 | 
			
		||||
    def test_drop_collection(self):
 | 
			
		||||
        """Ensure that the collection may be dropped from the database.
 | 
			
		||||
        """
 | 
			
		||||
        collection_name = 'person'
 | 
			
		||||
        self.Person(name='Test').save()
 | 
			
		||||
        collection_name = "person"
 | 
			
		||||
        self.Person(name="Test").save()
 | 
			
		||||
        self.assertIn(collection_name, list_collection_names(self.db))
 | 
			
		||||
 | 
			
		||||
        self.Person.drop_collection()
 | 
			
		||||
@@ -73,14 +74,16 @@ class ClassMethodsTest(unittest.TestCase):
 | 
			
		||||
        """Ensure that register delete rule adds a delete rule to the document
 | 
			
		||||
        meta.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Job(Document):
 | 
			
		||||
            employee = ReferenceField(self.Person)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(self.Person._meta.get('delete_rules'), None)
 | 
			
		||||
        self.assertEqual(self.Person._meta.get("delete_rules"), None)
 | 
			
		||||
 | 
			
		||||
        self.Person.register_delete_rule(Job, 'employee', NULLIFY)
 | 
			
		||||
        self.assertEqual(self.Person._meta['delete_rules'],
 | 
			
		||||
                         {(Job, 'employee'): NULLIFY})
 | 
			
		||||
        self.Person.register_delete_rule(Job, "employee", NULLIFY)
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            self.Person._meta["delete_rules"], {(Job, "employee"): NULLIFY}
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_compare_indexes(self):
 | 
			
		||||
        """ Ensure that the indexes are properly created and that
 | 
			
		||||
@@ -93,23 +96,27 @@ class ClassMethodsTest(unittest.TestCase):
 | 
			
		||||
            description = StringField()
 | 
			
		||||
            tags = StringField()
 | 
			
		||||
 | 
			
		||||
            meta = {
 | 
			
		||||
                'indexes': [('author', 'title')]
 | 
			
		||||
            }
 | 
			
		||||
            meta = {"indexes": [("author", "title")]}
 | 
			
		||||
 | 
			
		||||
        BlogPost.drop_collection()
 | 
			
		||||
 | 
			
		||||
        BlogPost.ensure_indexes()
 | 
			
		||||
        self.assertEqual(BlogPost.compare_indexes(), {'missing': [], 'extra': []})
 | 
			
		||||
        self.assertEqual(BlogPost.compare_indexes(), {"missing": [], "extra": []})
 | 
			
		||||
 | 
			
		||||
        BlogPost.ensure_index(['author', 'description'])
 | 
			
		||||
        self.assertEqual(BlogPost.compare_indexes(), {'missing': [], 'extra': [[('author', 1), ('description', 1)]]})
 | 
			
		||||
        BlogPost.ensure_index(["author", "description"])
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            BlogPost.compare_indexes(),
 | 
			
		||||
            {"missing": [], "extra": [[("author", 1), ("description", 1)]]},
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        BlogPost._get_collection().drop_index('author_1_description_1')
 | 
			
		||||
        self.assertEqual(BlogPost.compare_indexes(), {'missing': [], 'extra': []})
 | 
			
		||||
        BlogPost._get_collection().drop_index("author_1_description_1")
 | 
			
		||||
        self.assertEqual(BlogPost.compare_indexes(), {"missing": [], "extra": []})
 | 
			
		||||
 | 
			
		||||
        BlogPost._get_collection().drop_index('author_1_title_1')
 | 
			
		||||
        self.assertEqual(BlogPost.compare_indexes(), {'missing': [[('author', 1), ('title', 1)]], 'extra': []})
 | 
			
		||||
        BlogPost._get_collection().drop_index("author_1_title_1")
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            BlogPost.compare_indexes(),
 | 
			
		||||
            {"missing": [[("author", 1), ("title", 1)]], "extra": []},
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_compare_indexes_inheritance(self):
 | 
			
		||||
        """ Ensure that the indexes are properly created and that
 | 
			
		||||
@@ -122,32 +129,34 @@ class ClassMethodsTest(unittest.TestCase):
 | 
			
		||||
            title = StringField()
 | 
			
		||||
            description = StringField()
 | 
			
		||||
 | 
			
		||||
            meta = {
 | 
			
		||||
                'allow_inheritance': True
 | 
			
		||||
            }
 | 
			
		||||
            meta = {"allow_inheritance": True}
 | 
			
		||||
 | 
			
		||||
        class BlogPostWithTags(BlogPost):
 | 
			
		||||
            tags = StringField()
 | 
			
		||||
            tag_list = ListField(StringField())
 | 
			
		||||
 | 
			
		||||
            meta = {
 | 
			
		||||
                'indexes': [('author', 'tags')]
 | 
			
		||||
            }
 | 
			
		||||
            meta = {"indexes": [("author", "tags")]}
 | 
			
		||||
 | 
			
		||||
        BlogPost.drop_collection()
 | 
			
		||||
 | 
			
		||||
        BlogPost.ensure_indexes()
 | 
			
		||||
        BlogPostWithTags.ensure_indexes()
 | 
			
		||||
        self.assertEqual(BlogPost.compare_indexes(), {'missing': [], 'extra': []})
 | 
			
		||||
        self.assertEqual(BlogPost.compare_indexes(), {"missing": [], "extra": []})
 | 
			
		||||
 | 
			
		||||
        BlogPostWithTags.ensure_index(['author', 'tag_list'])
 | 
			
		||||
        self.assertEqual(BlogPost.compare_indexes(), {'missing': [], 'extra': [[('_cls', 1), ('author', 1), ('tag_list', 1)]]})
 | 
			
		||||
        BlogPostWithTags.ensure_index(["author", "tag_list"])
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            BlogPost.compare_indexes(),
 | 
			
		||||
            {"missing": [], "extra": [[("_cls", 1), ("author", 1), ("tag_list", 1)]]},
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        BlogPostWithTags._get_collection().drop_index('_cls_1_author_1_tag_list_1')
 | 
			
		||||
        self.assertEqual(BlogPost.compare_indexes(), {'missing': [], 'extra': []})
 | 
			
		||||
        BlogPostWithTags._get_collection().drop_index("_cls_1_author_1_tag_list_1")
 | 
			
		||||
        self.assertEqual(BlogPost.compare_indexes(), {"missing": [], "extra": []})
 | 
			
		||||
 | 
			
		||||
        BlogPostWithTags._get_collection().drop_index('_cls_1_author_1_tags_1')
 | 
			
		||||
        self.assertEqual(BlogPost.compare_indexes(), {'missing': [[('_cls', 1), ('author', 1), ('tags', 1)]], 'extra': []})
 | 
			
		||||
        BlogPostWithTags._get_collection().drop_index("_cls_1_author_1_tags_1")
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            BlogPost.compare_indexes(),
 | 
			
		||||
            {"missing": [[("_cls", 1), ("author", 1), ("tags", 1)]], "extra": []},
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_compare_indexes_multiple_subclasses(self):
 | 
			
		||||
        """ Ensure that compare_indexes behaves correctly if called from a
 | 
			
		||||
@@ -159,32 +168,30 @@ class ClassMethodsTest(unittest.TestCase):
 | 
			
		||||
            title = StringField()
 | 
			
		||||
            description = StringField()
 | 
			
		||||
 | 
			
		||||
            meta = {
 | 
			
		||||
                'allow_inheritance': True
 | 
			
		||||
            }
 | 
			
		||||
            meta = {"allow_inheritance": True}
 | 
			
		||||
 | 
			
		||||
        class BlogPostWithTags(BlogPost):
 | 
			
		||||
            tags = StringField()
 | 
			
		||||
            tag_list = ListField(StringField())
 | 
			
		||||
 | 
			
		||||
            meta = {
 | 
			
		||||
                'indexes': [('author', 'tags')]
 | 
			
		||||
            }
 | 
			
		||||
            meta = {"indexes": [("author", "tags")]}
 | 
			
		||||
 | 
			
		||||
        class BlogPostWithCustomField(BlogPost):
 | 
			
		||||
            custom = DictField()
 | 
			
		||||
 | 
			
		||||
            meta = {
 | 
			
		||||
                'indexes': [('author', 'custom')]
 | 
			
		||||
            }
 | 
			
		||||
            meta = {"indexes": [("author", "custom")]}
 | 
			
		||||
 | 
			
		||||
        BlogPost.ensure_indexes()
 | 
			
		||||
        BlogPostWithTags.ensure_indexes()
 | 
			
		||||
        BlogPostWithCustomField.ensure_indexes()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(BlogPost.compare_indexes(), {'missing': [], 'extra': []})
 | 
			
		||||
        self.assertEqual(BlogPostWithTags.compare_indexes(), {'missing': [], 'extra': []})
 | 
			
		||||
        self.assertEqual(BlogPostWithCustomField.compare_indexes(), {'missing': [], 'extra': []})
 | 
			
		||||
        self.assertEqual(BlogPost.compare_indexes(), {"missing": [], "extra": []})
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            BlogPostWithTags.compare_indexes(), {"missing": [], "extra": []}
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            BlogPostWithCustomField.compare_indexes(), {"missing": [], "extra": []}
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_compare_indexes_for_text_indexes(self):
 | 
			
		||||
        """ Ensure that compare_indexes behaves correctly for text indexes """
 | 
			
		||||
@@ -192,17 +199,20 @@ class ClassMethodsTest(unittest.TestCase):
 | 
			
		||||
        class Doc(Document):
 | 
			
		||||
            a = StringField()
 | 
			
		||||
            b = StringField()
 | 
			
		||||
            meta = {'indexes': [
 | 
			
		||||
                {'fields': ['$a', "$b"],
 | 
			
		||||
                 'default_language': 'english',
 | 
			
		||||
                 'weights': {'a': 10, 'b': 2}
 | 
			
		||||
                }
 | 
			
		||||
            ]}
 | 
			
		||||
            meta = {
 | 
			
		||||
                "indexes": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "fields": ["$a", "$b"],
 | 
			
		||||
                        "default_language": "english",
 | 
			
		||||
                        "weights": {"a": 10, "b": 2},
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        Doc.drop_collection()
 | 
			
		||||
        Doc.ensure_indexes()
 | 
			
		||||
        actual = Doc.compare_indexes()
 | 
			
		||||
        expected = {'missing': [], 'extra': []}
 | 
			
		||||
        expected = {"missing": [], "extra": []}
 | 
			
		||||
        self.assertEqual(actual, expected)
 | 
			
		||||
 | 
			
		||||
    def test_list_indexes_inheritance(self):
 | 
			
		||||
@@ -215,23 +225,17 @@ class ClassMethodsTest(unittest.TestCase):
 | 
			
		||||
            title = StringField()
 | 
			
		||||
            description = StringField()
 | 
			
		||||
 | 
			
		||||
            meta = {
 | 
			
		||||
                'allow_inheritance': True
 | 
			
		||||
            }
 | 
			
		||||
            meta = {"allow_inheritance": True}
 | 
			
		||||
 | 
			
		||||
        class BlogPostWithTags(BlogPost):
 | 
			
		||||
            tags = StringField()
 | 
			
		||||
 | 
			
		||||
            meta = {
 | 
			
		||||
                'indexes': [('author', 'tags')]
 | 
			
		||||
            }
 | 
			
		||||
            meta = {"indexes": [("author", "tags")]}
 | 
			
		||||
 | 
			
		||||
        class BlogPostWithTagsAndExtraText(BlogPostWithTags):
 | 
			
		||||
            extra_text = StringField()
 | 
			
		||||
 | 
			
		||||
            meta = {
 | 
			
		||||
                'indexes': [('author', 'tags', 'extra_text')]
 | 
			
		||||
            }
 | 
			
		||||
            meta = {"indexes": [("author", "tags", "extra_text")]}
 | 
			
		||||
 | 
			
		||||
        BlogPost.drop_collection()
 | 
			
		||||
 | 
			
		||||
@@ -239,17 +243,21 @@ class ClassMethodsTest(unittest.TestCase):
 | 
			
		||||
        BlogPostWithTags.ensure_indexes()
 | 
			
		||||
        BlogPostWithTagsAndExtraText.ensure_indexes()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(BlogPost.list_indexes(),
 | 
			
		||||
                         BlogPostWithTags.list_indexes())
 | 
			
		||||
        self.assertEqual(BlogPost.list_indexes(),
 | 
			
		||||
                         BlogPostWithTagsAndExtraText.list_indexes())
 | 
			
		||||
        self.assertEqual(BlogPost.list_indexes(),
 | 
			
		||||
                         [[('_cls', 1), ('author', 1), ('tags', 1)],
 | 
			
		||||
                         [('_cls', 1), ('author', 1), ('tags', 1), ('extra_text', 1)],
 | 
			
		||||
                         [(u'_id', 1)], [('_cls', 1)]])
 | 
			
		||||
        self.assertEqual(BlogPost.list_indexes(), BlogPostWithTags.list_indexes())
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            BlogPost.list_indexes(), BlogPostWithTagsAndExtraText.list_indexes()
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            BlogPost.list_indexes(),
 | 
			
		||||
            [
 | 
			
		||||
                [("_cls", 1), ("author", 1), ("tags", 1)],
 | 
			
		||||
                [("_cls", 1), ("author", 1), ("tags", 1), ("extra_text", 1)],
 | 
			
		||||
                [(u"_id", 1)],
 | 
			
		||||
                [("_cls", 1)],
 | 
			
		||||
            ],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_register_delete_rule_inherited(self):
 | 
			
		||||
 | 
			
		||||
        class Vaccine(Document):
 | 
			
		||||
            name = StringField(required=True)
 | 
			
		||||
 | 
			
		||||
@@ -257,15 +265,17 @@ class ClassMethodsTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
        class Animal(Document):
 | 
			
		||||
            family = StringField(required=True)
 | 
			
		||||
            vaccine_made = ListField(ReferenceField("Vaccine", reverse_delete_rule=PULL))
 | 
			
		||||
            vaccine_made = ListField(
 | 
			
		||||
                ReferenceField("Vaccine", reverse_delete_rule=PULL)
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            meta = {"allow_inheritance": True, "indexes": ["family"]}
 | 
			
		||||
 | 
			
		||||
        class Cat(Animal):
 | 
			
		||||
            name = StringField(required=True)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(Vaccine._meta['delete_rules'][(Animal, 'vaccine_made')], PULL)
 | 
			
		||||
        self.assertEqual(Vaccine._meta['delete_rules'][(Cat, 'vaccine_made')], PULL)
 | 
			
		||||
        self.assertEqual(Vaccine._meta["delete_rules"][(Animal, "vaccine_made")], PULL)
 | 
			
		||||
        self.assertEqual(Vaccine._meta["delete_rules"][(Cat, "vaccine_made")], PULL)
 | 
			
		||||
 | 
			
		||||
    def test_collection_naming(self):
 | 
			
		||||
        """Ensure that a collection with a specified name may be used.
 | 
			
		||||
@@ -273,74 +283,73 @@ class ClassMethodsTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
        class DefaultNamingTest(Document):
 | 
			
		||||
            pass
 | 
			
		||||
        self.assertEqual('default_naming_test',
 | 
			
		||||
                         DefaultNamingTest._get_collection_name())
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            "default_naming_test", DefaultNamingTest._get_collection_name()
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        class CustomNamingTest(Document):
 | 
			
		||||
            meta = {'collection': 'pimp_my_collection'}
 | 
			
		||||
            meta = {"collection": "pimp_my_collection"}
 | 
			
		||||
 | 
			
		||||
        self.assertEqual('pimp_my_collection',
 | 
			
		||||
                         CustomNamingTest._get_collection_name())
 | 
			
		||||
        self.assertEqual("pimp_my_collection", CustomNamingTest._get_collection_name())
 | 
			
		||||
 | 
			
		||||
        class DynamicNamingTest(Document):
 | 
			
		||||
            meta = {'collection': lambda c: "DYNAMO"}
 | 
			
		||||
        self.assertEqual('DYNAMO', DynamicNamingTest._get_collection_name())
 | 
			
		||||
            meta = {"collection": lambda c: "DYNAMO"}
 | 
			
		||||
 | 
			
		||||
        self.assertEqual("DYNAMO", DynamicNamingTest._get_collection_name())
 | 
			
		||||
 | 
			
		||||
        # Use Abstract class to handle backwards compatibility
 | 
			
		||||
        class BaseDocument(Document):
 | 
			
		||||
            meta = {
 | 
			
		||||
                'abstract': True,
 | 
			
		||||
                'collection': lambda c: c.__name__.lower()
 | 
			
		||||
            }
 | 
			
		||||
            meta = {"abstract": True, "collection": lambda c: c.__name__.lower()}
 | 
			
		||||
 | 
			
		||||
        class OldNamingConvention(BaseDocument):
 | 
			
		||||
            pass
 | 
			
		||||
        self.assertEqual('oldnamingconvention',
 | 
			
		||||
                         OldNamingConvention._get_collection_name())
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            "oldnamingconvention", OldNamingConvention._get_collection_name()
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        class InheritedAbstractNamingTest(BaseDocument):
 | 
			
		||||
            meta = {'collection': 'wibble'}
 | 
			
		||||
        self.assertEqual('wibble',
 | 
			
		||||
                         InheritedAbstractNamingTest._get_collection_name())
 | 
			
		||||
            meta = {"collection": "wibble"}
 | 
			
		||||
 | 
			
		||||
        self.assertEqual("wibble", InheritedAbstractNamingTest._get_collection_name())
 | 
			
		||||
 | 
			
		||||
        # Mixin tests
 | 
			
		||||
        class BaseMixin(object):
 | 
			
		||||
            meta = {
 | 
			
		||||
                'collection': lambda c: c.__name__.lower()
 | 
			
		||||
            }
 | 
			
		||||
            meta = {"collection": lambda c: c.__name__.lower()}
 | 
			
		||||
 | 
			
		||||
        class OldMixinNamingConvention(Document, BaseMixin):
 | 
			
		||||
            pass
 | 
			
		||||
        self.assertEqual('oldmixinnamingconvention',
 | 
			
		||||
                          OldMixinNamingConvention._get_collection_name())
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            "oldmixinnamingconvention", OldMixinNamingConvention._get_collection_name()
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        class BaseMixin(object):
 | 
			
		||||
            meta = {
 | 
			
		||||
                'collection': lambda c: c.__name__.lower()
 | 
			
		||||
            }
 | 
			
		||||
            meta = {"collection": lambda c: c.__name__.lower()}
 | 
			
		||||
 | 
			
		||||
        class BaseDocument(Document, BaseMixin):
 | 
			
		||||
            meta = {'allow_inheritance': True}
 | 
			
		||||
            meta = {"allow_inheritance": True}
 | 
			
		||||
 | 
			
		||||
        class MyDocument(BaseDocument):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        self.assertEqual('basedocument', MyDocument._get_collection_name())
 | 
			
		||||
        self.assertEqual("basedocument", MyDocument._get_collection_name())
 | 
			
		||||
 | 
			
		||||
    def test_custom_collection_name_operations(self):
 | 
			
		||||
        """Ensure that a collection with a specified name is used as expected.
 | 
			
		||||
        """
 | 
			
		||||
        collection_name = 'personCollTest'
 | 
			
		||||
        collection_name = "personCollTest"
 | 
			
		||||
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            meta = {'collection': collection_name}
 | 
			
		||||
            meta = {"collection": collection_name}
 | 
			
		||||
 | 
			
		||||
        Person(name="Test User").save()
 | 
			
		||||
        self.assertIn(collection_name, list_collection_names(self.db))
 | 
			
		||||
 | 
			
		||||
        user_obj = self.db[collection_name].find_one()
 | 
			
		||||
        self.assertEqual(user_obj['name'], "Test User")
 | 
			
		||||
        self.assertEqual(user_obj["name"], "Test User")
 | 
			
		||||
 | 
			
		||||
        user_obj = Person.objects[0]
 | 
			
		||||
        self.assertEqual(user_obj.name, "Test User")
 | 
			
		||||
@@ -354,7 +363,7 @@ class ClassMethodsTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            name = StringField(primary_key=True)
 | 
			
		||||
            meta = {'collection': 'app'}
 | 
			
		||||
            meta = {"collection": "app"}
 | 
			
		||||
 | 
			
		||||
        Person(name="Test User").save()
 | 
			
		||||
 | 
			
		||||
@@ -364,5 +373,5 @@ class ClassMethodsTest(unittest.TestCase):
 | 
			
		||||
        Person.drop_collection()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    unittest.main()
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -3,17 +3,16 @@ import unittest
 | 
			
		||||
from mongoengine import *
 | 
			
		||||
from tests.utils import MongoDBTestCase
 | 
			
		||||
 | 
			
		||||
__all__ = ("TestDynamicDocument", )
 | 
			
		||||
__all__ = ("TestDynamicDocument",)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestDynamicDocument(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(TestDynamicDocument, self).setUp()
 | 
			
		||||
 | 
			
		||||
        class Person(DynamicDocument):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            meta = {'allow_inheritance': True}
 | 
			
		||||
            meta = {"allow_inheritance": True}
 | 
			
		||||
 | 
			
		||||
        Person.drop_collection()
 | 
			
		||||
 | 
			
		||||
@@ -26,8 +25,7 @@ class TestDynamicDocument(MongoDBTestCase):
 | 
			
		||||
        p.name = "James"
 | 
			
		||||
        p.age = 34
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(p.to_mongo(), {"_cls": "Person", "name": "James",
 | 
			
		||||
                                        "age": 34})
 | 
			
		||||
        self.assertEqual(p.to_mongo(), {"_cls": "Person", "name": "James", "age": 34})
 | 
			
		||||
        self.assertEqual(p.to_mongo().keys(), ["_cls", "name", "age"])
 | 
			
		||||
        p.save()
 | 
			
		||||
        self.assertEqual(p.to_mongo().keys(), ["_id", "_cls", "name", "age"])
 | 
			
		||||
@@ -35,7 +33,7 @@ class TestDynamicDocument(MongoDBTestCase):
 | 
			
		||||
        self.assertEqual(self.Person.objects.first().age, 34)
 | 
			
		||||
 | 
			
		||||
        # Confirm no changes to self.Person
 | 
			
		||||
        self.assertFalse(hasattr(self.Person, 'age'))
 | 
			
		||||
        self.assertFalse(hasattr(self.Person, "age"))
 | 
			
		||||
 | 
			
		||||
    def test_change_scope_of_variable(self):
 | 
			
		||||
        """Test changing the scope of a dynamic field has no adverse effects"""
 | 
			
		||||
@@ -45,11 +43,11 @@ class TestDynamicDocument(MongoDBTestCase):
 | 
			
		||||
        p.save()
 | 
			
		||||
 | 
			
		||||
        p = self.Person.objects.get()
 | 
			
		||||
        p.misc = {'hello': 'world'}
 | 
			
		||||
        p.misc = {"hello": "world"}
 | 
			
		||||
        p.save()
 | 
			
		||||
 | 
			
		||||
        p = self.Person.objects.get()
 | 
			
		||||
        self.assertEqual(p.misc, {'hello': 'world'})
 | 
			
		||||
        self.assertEqual(p.misc, {"hello": "world"})
 | 
			
		||||
 | 
			
		||||
    def test_delete_dynamic_field(self):
 | 
			
		||||
        """Test deleting a dynamic field works"""
 | 
			
		||||
@@ -60,23 +58,23 @@ class TestDynamicDocument(MongoDBTestCase):
 | 
			
		||||
        p.save()
 | 
			
		||||
 | 
			
		||||
        p = self.Person.objects.get()
 | 
			
		||||
        p.misc = {'hello': 'world'}
 | 
			
		||||
        p.misc = {"hello": "world"}
 | 
			
		||||
        p.save()
 | 
			
		||||
 | 
			
		||||
        p = self.Person.objects.get()
 | 
			
		||||
        self.assertEqual(p.misc, {'hello': 'world'})
 | 
			
		||||
        self.assertEqual(p.misc, {"hello": "world"})
 | 
			
		||||
        collection = self.db[self.Person._get_collection_name()]
 | 
			
		||||
        obj = collection.find_one()
 | 
			
		||||
        self.assertEqual(sorted(obj.keys()), ['_cls', '_id', 'misc', 'name'])
 | 
			
		||||
        self.assertEqual(sorted(obj.keys()), ["_cls", "_id", "misc", "name"])
 | 
			
		||||
 | 
			
		||||
        del p.misc
 | 
			
		||||
        p.save()
 | 
			
		||||
 | 
			
		||||
        p = self.Person.objects.get()
 | 
			
		||||
        self.assertFalse(hasattr(p, 'misc'))
 | 
			
		||||
        self.assertFalse(hasattr(p, "misc"))
 | 
			
		||||
 | 
			
		||||
        obj = collection.find_one()
 | 
			
		||||
        self.assertEqual(sorted(obj.keys()), ['_cls', '_id', 'name'])
 | 
			
		||||
        self.assertEqual(sorted(obj.keys()), ["_cls", "_id", "name"])
 | 
			
		||||
 | 
			
		||||
    def test_reload_after_unsetting(self):
 | 
			
		||||
        p = self.Person()
 | 
			
		||||
@@ -91,77 +89,52 @@ class TestDynamicDocument(MongoDBTestCase):
 | 
			
		||||
        p.update(age=1)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(len(p._data), 3)
 | 
			
		||||
        self.assertEqual(sorted(p._data.keys()), ['_cls', 'id', 'name'])
 | 
			
		||||
        self.assertEqual(sorted(p._data.keys()), ["_cls", "id", "name"])
 | 
			
		||||
 | 
			
		||||
        p.reload()
 | 
			
		||||
        self.assertEqual(len(p._data), 4)
 | 
			
		||||
        self.assertEqual(sorted(p._data.keys()), ['_cls', 'age', 'id', 'name'])
 | 
			
		||||
        self.assertEqual(sorted(p._data.keys()), ["_cls", "age", "id", "name"])
 | 
			
		||||
 | 
			
		||||
    def test_fields_without_underscore(self):
 | 
			
		||||
        """Ensure we can query dynamic fields"""
 | 
			
		||||
        Person = self.Person
 | 
			
		||||
 | 
			
		||||
        p = self.Person(name='Dean')
 | 
			
		||||
        p = self.Person(name="Dean")
 | 
			
		||||
        p.save()
 | 
			
		||||
 | 
			
		||||
        raw_p = Person.objects.as_pymongo().get(id=p.id)
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            raw_p,
 | 
			
		||||
            {
 | 
			
		||||
                '_cls': u'Person',
 | 
			
		||||
                '_id': p.id,
 | 
			
		||||
                'name': u'Dean'
 | 
			
		||||
             }
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(raw_p, {"_cls": u"Person", "_id": p.id, "name": u"Dean"})
 | 
			
		||||
 | 
			
		||||
        p.name = 'OldDean'
 | 
			
		||||
        p.newattr = 'garbage'
 | 
			
		||||
        p.name = "OldDean"
 | 
			
		||||
        p.newattr = "garbage"
 | 
			
		||||
        p.save()
 | 
			
		||||
        raw_p = Person.objects.as_pymongo().get(id=p.id)
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            raw_p,
 | 
			
		||||
            {
 | 
			
		||||
                '_cls': u'Person',
 | 
			
		||||
                '_id': p.id,
 | 
			
		||||
                'name': 'OldDean',
 | 
			
		||||
                'newattr': u'garbage'
 | 
			
		||||
            }
 | 
			
		||||
            {"_cls": u"Person", "_id": p.id, "name": "OldDean", "newattr": u"garbage"},
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_fields_containing_underscore(self):
 | 
			
		||||
        """Ensure we can query dynamic fields"""
 | 
			
		||||
 | 
			
		||||
        class WeirdPerson(DynamicDocument):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            _name = StringField()
 | 
			
		||||
 | 
			
		||||
        WeirdPerson.drop_collection()
 | 
			
		||||
 | 
			
		||||
        p = WeirdPerson(name='Dean', _name='Dean')
 | 
			
		||||
        p = WeirdPerson(name="Dean", _name="Dean")
 | 
			
		||||
        p.save()
 | 
			
		||||
 | 
			
		||||
        raw_p = WeirdPerson.objects.as_pymongo().get(id=p.id)
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            raw_p,
 | 
			
		||||
            {
 | 
			
		||||
                '_id': p.id,
 | 
			
		||||
                '_name': u'Dean',
 | 
			
		||||
                'name': u'Dean'
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(raw_p, {"_id": p.id, "_name": u"Dean", "name": u"Dean"})
 | 
			
		||||
 | 
			
		||||
        p.name = 'OldDean'
 | 
			
		||||
        p._name = 'NewDean'
 | 
			
		||||
        p._newattr1 = 'garbage'    # Unknown fields won't be added
 | 
			
		||||
        p.name = "OldDean"
 | 
			
		||||
        p._name = "NewDean"
 | 
			
		||||
        p._newattr1 = "garbage"  # Unknown fields won't be added
 | 
			
		||||
        p.save()
 | 
			
		||||
        raw_p = WeirdPerson.objects.as_pymongo().get(id=p.id)
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            raw_p,
 | 
			
		||||
            {
 | 
			
		||||
                '_id': p.id,
 | 
			
		||||
                '_name': u'NewDean',
 | 
			
		||||
                'name': u'OldDean',
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(raw_p, {"_id": p.id, "_name": u"NewDean", "name": u"OldDean"})
 | 
			
		||||
 | 
			
		||||
    def test_dynamic_document_queries(self):
 | 
			
		||||
        """Ensure we can query dynamic fields"""
 | 
			
		||||
@@ -193,26 +166,25 @@ class TestDynamicDocument(MongoDBTestCase):
 | 
			
		||||
        p2.age = 10
 | 
			
		||||
        p2.save()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(Person.objects(age__icontains='ten').count(), 2)
 | 
			
		||||
        self.assertEqual(Person.objects(age__icontains="ten").count(), 2)
 | 
			
		||||
        self.assertEqual(Person.objects(age__gte=10).count(), 1)
 | 
			
		||||
 | 
			
		||||
    def test_complex_data_lookups(self):
 | 
			
		||||
        """Ensure you can query dynamic document dynamic fields"""
 | 
			
		||||
        p = self.Person()
 | 
			
		||||
        p.misc = {'hello': 'world'}
 | 
			
		||||
        p.misc = {"hello": "world"}
 | 
			
		||||
        p.save()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(1, self.Person.objects(misc__hello='world').count())
 | 
			
		||||
        self.assertEqual(1, self.Person.objects(misc__hello="world").count())
 | 
			
		||||
 | 
			
		||||
    def test_three_level_complex_data_lookups(self):
 | 
			
		||||
        """Ensure you can query three level document dynamic fields"""
 | 
			
		||||
        p = self.Person.objects.create(
 | 
			
		||||
            misc={'hello': {'hello2': 'world'}}
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(1, self.Person.objects(misc__hello__hello2='world').count())
 | 
			
		||||
        p = self.Person.objects.create(misc={"hello": {"hello2": "world"}})
 | 
			
		||||
        self.assertEqual(1, self.Person.objects(misc__hello__hello2="world").count())
 | 
			
		||||
 | 
			
		||||
    def test_complex_embedded_document_validation(self):
 | 
			
		||||
        """Ensure embedded dynamic documents may be validated"""
 | 
			
		||||
 | 
			
		||||
        class Embedded(DynamicEmbeddedDocument):
 | 
			
		||||
            content = URLField()
 | 
			
		||||
 | 
			
		||||
@@ -222,10 +194,10 @@ class TestDynamicDocument(MongoDBTestCase):
 | 
			
		||||
        Doc.drop_collection()
 | 
			
		||||
        doc = Doc()
 | 
			
		||||
 | 
			
		||||
        embedded_doc_1 = Embedded(content='http://mongoengine.org')
 | 
			
		||||
        embedded_doc_1 = Embedded(content="http://mongoengine.org")
 | 
			
		||||
        embedded_doc_1.validate()
 | 
			
		||||
 | 
			
		||||
        embedded_doc_2 = Embedded(content='this is not a url')
 | 
			
		||||
        embedded_doc_2 = Embedded(content="this is not a url")
 | 
			
		||||
        self.assertRaises(ValidationError, embedded_doc_2.validate)
 | 
			
		||||
 | 
			
		||||
        doc.embedded_field_1 = embedded_doc_1
 | 
			
		||||
@@ -234,15 +206,17 @@ class TestDynamicDocument(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_inheritance(self):
 | 
			
		||||
        """Ensure that dynamic document plays nice with inheritance"""
 | 
			
		||||
 | 
			
		||||
        class Employee(self.Person):
 | 
			
		||||
            salary = IntField()
 | 
			
		||||
 | 
			
		||||
        Employee.drop_collection()
 | 
			
		||||
 | 
			
		||||
        self.assertIn('name', Employee._fields)
 | 
			
		||||
        self.assertIn('salary', Employee._fields)
 | 
			
		||||
        self.assertEqual(Employee._get_collection_name(),
 | 
			
		||||
                         self.Person._get_collection_name())
 | 
			
		||||
        self.assertIn("name", Employee._fields)
 | 
			
		||||
        self.assertIn("salary", Employee._fields)
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            Employee._get_collection_name(), self.Person._get_collection_name()
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        joe_bloggs = Employee()
 | 
			
		||||
        joe_bloggs.name = "Joe Bloggs"
 | 
			
		||||
@@ -258,6 +232,7 @@ class TestDynamicDocument(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_embedded_dynamic_document(self):
 | 
			
		||||
        """Test dynamic embedded documents"""
 | 
			
		||||
 | 
			
		||||
        class Embedded(DynamicEmbeddedDocument):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
@@ -268,78 +243,88 @@ class TestDynamicDocument(MongoDBTestCase):
 | 
			
		||||
        doc = Doc()
 | 
			
		||||
 | 
			
		||||
        embedded_1 = Embedded()
 | 
			
		||||
        embedded_1.string_field = 'hello'
 | 
			
		||||
        embedded_1.string_field = "hello"
 | 
			
		||||
        embedded_1.int_field = 1
 | 
			
		||||
        embedded_1.dict_field = {'hello': 'world'}
 | 
			
		||||
        embedded_1.list_field = ['1', 2, {'hello': 'world'}]
 | 
			
		||||
        embedded_1.dict_field = {"hello": "world"}
 | 
			
		||||
        embedded_1.list_field = ["1", 2, {"hello": "world"}]
 | 
			
		||||
        doc.embedded_field = embedded_1
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(doc.to_mongo(), {
 | 
			
		||||
            "embedded_field": {
 | 
			
		||||
                "_cls": "Embedded",
 | 
			
		||||
                "string_field": "hello",
 | 
			
		||||
                "int_field": 1,
 | 
			
		||||
                "dict_field": {"hello": "world"},
 | 
			
		||||
                "list_field": ['1', 2, {'hello': 'world'}]
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        doc.save()
 | 
			
		||||
 | 
			
		||||
        doc = Doc.objects.first()
 | 
			
		||||
        self.assertEqual(doc.embedded_field.__class__, Embedded)
 | 
			
		||||
        self.assertEqual(doc.embedded_field.string_field, "hello")
 | 
			
		||||
        self.assertEqual(doc.embedded_field.int_field, 1)
 | 
			
		||||
        self.assertEqual(doc.embedded_field.dict_field, {'hello': 'world'})
 | 
			
		||||
        self.assertEqual(doc.embedded_field.list_field,
 | 
			
		||||
                            ['1', 2, {'hello': 'world'}])
 | 
			
		||||
 | 
			
		||||
    def test_complex_embedded_documents(self):
 | 
			
		||||
        """Test complex dynamic embedded documents setups"""
 | 
			
		||||
        class Embedded(DynamicEmbeddedDocument):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        class Doc(DynamicDocument):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        Doc.drop_collection()
 | 
			
		||||
        doc = Doc()
 | 
			
		||||
 | 
			
		||||
        embedded_1 = Embedded()
 | 
			
		||||
        embedded_1.string_field = 'hello'
 | 
			
		||||
        embedded_1.int_field = 1
 | 
			
		||||
        embedded_1.dict_field = {'hello': 'world'}
 | 
			
		||||
 | 
			
		||||
        embedded_2 = Embedded()
 | 
			
		||||
        embedded_2.string_field = 'hello'
 | 
			
		||||
        embedded_2.int_field = 1
 | 
			
		||||
        embedded_2.dict_field = {'hello': 'world'}
 | 
			
		||||
        embedded_2.list_field = ['1', 2, {'hello': 'world'}]
 | 
			
		||||
 | 
			
		||||
        embedded_1.list_field = ['1', 2, embedded_2]
 | 
			
		||||
        doc.embedded_field = embedded_1
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(doc.to_mongo(), {
 | 
			
		||||
            "embedded_field": {
 | 
			
		||||
                "_cls": "Embedded",
 | 
			
		||||
                "string_field": "hello",
 | 
			
		||||
                "int_field": 1,
 | 
			
		||||
                "dict_field": {"hello": "world"},
 | 
			
		||||
                "list_field": ['1', 2,
 | 
			
		||||
                    {"_cls": "Embedded",
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            doc.to_mongo(),
 | 
			
		||||
            {
 | 
			
		||||
                "embedded_field": {
 | 
			
		||||
                    "_cls": "Embedded",
 | 
			
		||||
                    "string_field": "hello",
 | 
			
		||||
                    "int_field": 1,
 | 
			
		||||
                    "dict_field": {"hello": "world"},
 | 
			
		||||
                    "list_field": ['1', 2, {'hello': 'world'}]}
 | 
			
		||||
                ]
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
                    "list_field": ["1", 2, {"hello": "world"}],
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
        doc.save()
 | 
			
		||||
 | 
			
		||||
        doc = Doc.objects.first()
 | 
			
		||||
        self.assertEqual(doc.embedded_field.__class__, Embedded)
 | 
			
		||||
        self.assertEqual(doc.embedded_field.string_field, "hello")
 | 
			
		||||
        self.assertEqual(doc.embedded_field.int_field, 1)
 | 
			
		||||
        self.assertEqual(doc.embedded_field.dict_field, {"hello": "world"})
 | 
			
		||||
        self.assertEqual(doc.embedded_field.list_field, ["1", 2, {"hello": "world"}])
 | 
			
		||||
 | 
			
		||||
    def test_complex_embedded_documents(self):
 | 
			
		||||
        """Test complex dynamic embedded documents setups"""
 | 
			
		||||
 | 
			
		||||
        class Embedded(DynamicEmbeddedDocument):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        class Doc(DynamicDocument):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        Doc.drop_collection()
 | 
			
		||||
        doc = Doc()
 | 
			
		||||
 | 
			
		||||
        embedded_1 = Embedded()
 | 
			
		||||
        embedded_1.string_field = "hello"
 | 
			
		||||
        embedded_1.int_field = 1
 | 
			
		||||
        embedded_1.dict_field = {"hello": "world"}
 | 
			
		||||
 | 
			
		||||
        embedded_2 = Embedded()
 | 
			
		||||
        embedded_2.string_field = "hello"
 | 
			
		||||
        embedded_2.int_field = 1
 | 
			
		||||
        embedded_2.dict_field = {"hello": "world"}
 | 
			
		||||
        embedded_2.list_field = ["1", 2, {"hello": "world"}]
 | 
			
		||||
 | 
			
		||||
        embedded_1.list_field = ["1", 2, embedded_2]
 | 
			
		||||
        doc.embedded_field = embedded_1
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            doc.to_mongo(),
 | 
			
		||||
            {
 | 
			
		||||
                "embedded_field": {
 | 
			
		||||
                    "_cls": "Embedded",
 | 
			
		||||
                    "string_field": "hello",
 | 
			
		||||
                    "int_field": 1,
 | 
			
		||||
                    "dict_field": {"hello": "world"},
 | 
			
		||||
                    "list_field": [
 | 
			
		||||
                        "1",
 | 
			
		||||
                        2,
 | 
			
		||||
                        {
 | 
			
		||||
                            "_cls": "Embedded",
 | 
			
		||||
                            "string_field": "hello",
 | 
			
		||||
                            "int_field": 1,
 | 
			
		||||
                            "dict_field": {"hello": "world"},
 | 
			
		||||
                            "list_field": ["1", 2, {"hello": "world"}],
 | 
			
		||||
                        },
 | 
			
		||||
                    ],
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
        doc.save()
 | 
			
		||||
        doc = Doc.objects.first()
 | 
			
		||||
        self.assertEqual(doc.embedded_field.__class__, Embedded)
 | 
			
		||||
        self.assertEqual(doc.embedded_field.string_field, "hello")
 | 
			
		||||
        self.assertEqual(doc.embedded_field.int_field, 1)
 | 
			
		||||
        self.assertEqual(doc.embedded_field.dict_field, {'hello': 'world'})
 | 
			
		||||
        self.assertEqual(doc.embedded_field.list_field[0], '1')
 | 
			
		||||
        self.assertEqual(doc.embedded_field.dict_field, {"hello": "world"})
 | 
			
		||||
        self.assertEqual(doc.embedded_field.list_field[0], "1")
 | 
			
		||||
        self.assertEqual(doc.embedded_field.list_field[1], 2)
 | 
			
		||||
 | 
			
		||||
        embedded_field = doc.embedded_field.list_field[2]
 | 
			
		||||
@@ -347,9 +332,8 @@ class TestDynamicDocument(MongoDBTestCase):
 | 
			
		||||
        self.assertEqual(embedded_field.__class__, Embedded)
 | 
			
		||||
        self.assertEqual(embedded_field.string_field, "hello")
 | 
			
		||||
        self.assertEqual(embedded_field.int_field, 1)
 | 
			
		||||
        self.assertEqual(embedded_field.dict_field, {'hello': 'world'})
 | 
			
		||||
        self.assertEqual(embedded_field.list_field, ['1', 2,
 | 
			
		||||
                                                        {'hello': 'world'}])
 | 
			
		||||
        self.assertEqual(embedded_field.dict_field, {"hello": "world"})
 | 
			
		||||
        self.assertEqual(embedded_field.list_field, ["1", 2, {"hello": "world"}])
 | 
			
		||||
 | 
			
		||||
    def test_dynamic_and_embedded(self):
 | 
			
		||||
        """Ensure embedded documents play nicely"""
 | 
			
		||||
@@ -392,10 +376,15 @@ class TestDynamicDocument(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        Person.drop_collection()
 | 
			
		||||
 | 
			
		||||
        Person(name="Eric", address=Address(city="San Francisco", street_number="1337")).save()
 | 
			
		||||
        Person(
 | 
			
		||||
            name="Eric", address=Address(city="San Francisco", street_number="1337")
 | 
			
		||||
        ).save()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(Person.objects.first().address.street_number, '1337')
 | 
			
		||||
        self.assertEqual(Person.objects.only('address__street_number').first().address.street_number, '1337')
 | 
			
		||||
        self.assertEqual(Person.objects.first().address.street_number, "1337")
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            Person.objects.only("address__street_number").first().address.street_number,
 | 
			
		||||
            "1337",
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_dynamic_and_embedded_dict_access(self):
 | 
			
		||||
        """Ensure embedded dynamic documents work with dict[] style access"""
 | 
			
		||||
@@ -435,5 +424,5 @@ class TestDynamicDocument(MongoDBTestCase):
 | 
			
		||||
        self.assertEqual(Person.objects.first().age, 35)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    unittest.main()
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -4,18 +4,24 @@ import warnings
 | 
			
		||||
 | 
			
		||||
from six import iteritems
 | 
			
		||||
 | 
			
		||||
from mongoengine import (BooleanField, Document, EmbeddedDocument,
 | 
			
		||||
                         EmbeddedDocumentField, GenericReferenceField,
 | 
			
		||||
                         IntField, ReferenceField, StringField)
 | 
			
		||||
from mongoengine import (
 | 
			
		||||
    BooleanField,
 | 
			
		||||
    Document,
 | 
			
		||||
    EmbeddedDocument,
 | 
			
		||||
    EmbeddedDocumentField,
 | 
			
		||||
    GenericReferenceField,
 | 
			
		||||
    IntField,
 | 
			
		||||
    ReferenceField,
 | 
			
		||||
    StringField,
 | 
			
		||||
)
 | 
			
		||||
from mongoengine.pymongo_support import list_collection_names
 | 
			
		||||
from tests.utils import MongoDBTestCase
 | 
			
		||||
from tests.fixtures import Base
 | 
			
		||||
 | 
			
		||||
__all__ = ('InheritanceTest', )
 | 
			
		||||
__all__ = ("InheritanceTest",)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class InheritanceTest(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
    def tearDown(self):
 | 
			
		||||
        for collection in list_collection_names(self.db):
 | 
			
		||||
            self.db.drop_collection(collection)
 | 
			
		||||
@@ -25,16 +31,16 @@ class InheritanceTest(MongoDBTestCase):
 | 
			
		||||
        # and when object gets reloaded (prevent regression of #1950)
 | 
			
		||||
        class EmbedData(EmbeddedDocument):
 | 
			
		||||
            data = StringField()
 | 
			
		||||
            meta = {'allow_inheritance': True}
 | 
			
		||||
            meta = {"allow_inheritance": True}
 | 
			
		||||
 | 
			
		||||
        class DataDoc(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            embed = EmbeddedDocumentField(EmbedData)
 | 
			
		||||
            meta = {'allow_inheritance': True}
 | 
			
		||||
            meta = {"allow_inheritance": True}
 | 
			
		||||
 | 
			
		||||
        test_doc = DataDoc(name='test', embed=EmbedData(data='data'))
 | 
			
		||||
        self.assertEqual(test_doc._cls, 'DataDoc')
 | 
			
		||||
        self.assertEqual(test_doc.embed._cls, 'EmbedData')
 | 
			
		||||
        test_doc = DataDoc(name="test", embed=EmbedData(data="data"))
 | 
			
		||||
        self.assertEqual(test_doc._cls, "DataDoc")
 | 
			
		||||
        self.assertEqual(test_doc.embed._cls, "EmbedData")
 | 
			
		||||
        test_doc.save()
 | 
			
		||||
        saved_doc = DataDoc.objects.with_id(test_doc.id)
 | 
			
		||||
        self.assertEqual(test_doc._cls, saved_doc._cls)
 | 
			
		||||
@@ -44,163 +50,234 @@ class InheritanceTest(MongoDBTestCase):
 | 
			
		||||
    def test_superclasses(self):
 | 
			
		||||
        """Ensure that the correct list of superclasses is assembled.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Animal(Document):
 | 
			
		||||
            meta = {'allow_inheritance': True}
 | 
			
		||||
        class Fish(Animal): pass
 | 
			
		||||
        class Guppy(Fish): pass
 | 
			
		||||
        class Mammal(Animal): pass
 | 
			
		||||
        class Dog(Mammal): pass
 | 
			
		||||
        class Human(Mammal): pass
 | 
			
		||||
            meta = {"allow_inheritance": True}
 | 
			
		||||
 | 
			
		||||
        class Fish(Animal):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        class Guppy(Fish):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        class Mammal(Animal):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        class Dog(Mammal):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        class Human(Mammal):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(Animal._superclasses, ())
 | 
			
		||||
        self.assertEqual(Fish._superclasses, ('Animal',))
 | 
			
		||||
        self.assertEqual(Guppy._superclasses, ('Animal', 'Animal.Fish'))
 | 
			
		||||
        self.assertEqual(Mammal._superclasses, ('Animal',))
 | 
			
		||||
        self.assertEqual(Dog._superclasses, ('Animal', 'Animal.Mammal'))
 | 
			
		||||
        self.assertEqual(Human._superclasses, ('Animal', 'Animal.Mammal'))
 | 
			
		||||
        self.assertEqual(Fish._superclasses, ("Animal",))
 | 
			
		||||
        self.assertEqual(Guppy._superclasses, ("Animal", "Animal.Fish"))
 | 
			
		||||
        self.assertEqual(Mammal._superclasses, ("Animal",))
 | 
			
		||||
        self.assertEqual(Dog._superclasses, ("Animal", "Animal.Mammal"))
 | 
			
		||||
        self.assertEqual(Human._superclasses, ("Animal", "Animal.Mammal"))
 | 
			
		||||
 | 
			
		||||
    def test_external_superclasses(self):
 | 
			
		||||
        """Ensure that the correct list of super classes is assembled when
 | 
			
		||||
        importing part of the model.
 | 
			
		||||
        """
 | 
			
		||||
        class Animal(Base): pass
 | 
			
		||||
        class Fish(Animal): pass
 | 
			
		||||
        class Guppy(Fish): pass
 | 
			
		||||
        class Mammal(Animal): pass
 | 
			
		||||
        class Dog(Mammal): pass
 | 
			
		||||
        class Human(Mammal): pass
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(Animal._superclasses, ('Base', ))
 | 
			
		||||
        self.assertEqual(Fish._superclasses, ('Base', 'Base.Animal',))
 | 
			
		||||
        self.assertEqual(Guppy._superclasses, ('Base', 'Base.Animal',
 | 
			
		||||
                                               'Base.Animal.Fish'))
 | 
			
		||||
        self.assertEqual(Mammal._superclasses, ('Base', 'Base.Animal',))
 | 
			
		||||
        self.assertEqual(Dog._superclasses, ('Base', 'Base.Animal',
 | 
			
		||||
                                             'Base.Animal.Mammal'))
 | 
			
		||||
        self.assertEqual(Human._superclasses, ('Base', 'Base.Animal',
 | 
			
		||||
                                               'Base.Animal.Mammal'))
 | 
			
		||||
        class Animal(Base):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        class Fish(Animal):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        class Guppy(Fish):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        class Mammal(Animal):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        class Dog(Mammal):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        class Human(Mammal):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(Animal._superclasses, ("Base",))
 | 
			
		||||
        self.assertEqual(Fish._superclasses, ("Base", "Base.Animal"))
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            Guppy._superclasses, ("Base", "Base.Animal", "Base.Animal.Fish")
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(Mammal._superclasses, ("Base", "Base.Animal"))
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            Dog._superclasses, ("Base", "Base.Animal", "Base.Animal.Mammal")
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            Human._superclasses, ("Base", "Base.Animal", "Base.Animal.Mammal")
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_subclasses(self):
 | 
			
		||||
        """Ensure that the correct list of _subclasses (subclasses) is
 | 
			
		||||
        assembled.
 | 
			
		||||
        """
 | 
			
		||||
        class Animal(Document):
 | 
			
		||||
            meta = {'allow_inheritance': True}
 | 
			
		||||
        class Fish(Animal): pass
 | 
			
		||||
        class Guppy(Fish): pass
 | 
			
		||||
        class Mammal(Animal): pass
 | 
			
		||||
        class Dog(Mammal): pass
 | 
			
		||||
        class Human(Mammal): pass
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(Animal._subclasses, ('Animal',
 | 
			
		||||
                                         'Animal.Fish',
 | 
			
		||||
                                         'Animal.Fish.Guppy',
 | 
			
		||||
                                         'Animal.Mammal',
 | 
			
		||||
                                         'Animal.Mammal.Dog',
 | 
			
		||||
                                         'Animal.Mammal.Human'))
 | 
			
		||||
        self.assertEqual(Fish._subclasses, ('Animal.Fish',
 | 
			
		||||
                                       'Animal.Fish.Guppy',))
 | 
			
		||||
        self.assertEqual(Guppy._subclasses, ('Animal.Fish.Guppy',))
 | 
			
		||||
        self.assertEqual(Mammal._subclasses, ('Animal.Mammal',
 | 
			
		||||
                                         'Animal.Mammal.Dog',
 | 
			
		||||
                                         'Animal.Mammal.Human'))
 | 
			
		||||
        self.assertEqual(Human._subclasses, ('Animal.Mammal.Human',))
 | 
			
		||||
        class Animal(Document):
 | 
			
		||||
            meta = {"allow_inheritance": True}
 | 
			
		||||
 | 
			
		||||
        class Fish(Animal):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        class Guppy(Fish):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        class Mammal(Animal):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        class Dog(Mammal):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        class Human(Mammal):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            Animal._subclasses,
 | 
			
		||||
            (
 | 
			
		||||
                "Animal",
 | 
			
		||||
                "Animal.Fish",
 | 
			
		||||
                "Animal.Fish.Guppy",
 | 
			
		||||
                "Animal.Mammal",
 | 
			
		||||
                "Animal.Mammal.Dog",
 | 
			
		||||
                "Animal.Mammal.Human",
 | 
			
		||||
            ),
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(Fish._subclasses, ("Animal.Fish", "Animal.Fish.Guppy"))
 | 
			
		||||
        self.assertEqual(Guppy._subclasses, ("Animal.Fish.Guppy",))
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            Mammal._subclasses,
 | 
			
		||||
            ("Animal.Mammal", "Animal.Mammal.Dog", "Animal.Mammal.Human"),
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(Human._subclasses, ("Animal.Mammal.Human",))
 | 
			
		||||
 | 
			
		||||
    def test_external_subclasses(self):
 | 
			
		||||
        """Ensure that the correct list of _subclasses (subclasses) is
 | 
			
		||||
        assembled when importing part of the model.
 | 
			
		||||
        """
 | 
			
		||||
        class Animal(Base): pass
 | 
			
		||||
        class Fish(Animal): pass
 | 
			
		||||
        class Guppy(Fish): pass
 | 
			
		||||
        class Mammal(Animal): pass
 | 
			
		||||
        class Dog(Mammal): pass
 | 
			
		||||
        class Human(Mammal): pass
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(Animal._subclasses, ('Base.Animal',
 | 
			
		||||
                                              'Base.Animal.Fish',
 | 
			
		||||
                                              'Base.Animal.Fish.Guppy',
 | 
			
		||||
                                              'Base.Animal.Mammal',
 | 
			
		||||
                                              'Base.Animal.Mammal.Dog',
 | 
			
		||||
                                              'Base.Animal.Mammal.Human'))
 | 
			
		||||
        self.assertEqual(Fish._subclasses, ('Base.Animal.Fish',
 | 
			
		||||
                                            'Base.Animal.Fish.Guppy',))
 | 
			
		||||
        self.assertEqual(Guppy._subclasses, ('Base.Animal.Fish.Guppy',))
 | 
			
		||||
        self.assertEqual(Mammal._subclasses, ('Base.Animal.Mammal',
 | 
			
		||||
                                              'Base.Animal.Mammal.Dog',
 | 
			
		||||
                                              'Base.Animal.Mammal.Human'))
 | 
			
		||||
        self.assertEqual(Human._subclasses, ('Base.Animal.Mammal.Human',))
 | 
			
		||||
        class Animal(Base):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        class Fish(Animal):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        class Guppy(Fish):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        class Mammal(Animal):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        class Dog(Mammal):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        class Human(Mammal):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            Animal._subclasses,
 | 
			
		||||
            (
 | 
			
		||||
                "Base.Animal",
 | 
			
		||||
                "Base.Animal.Fish",
 | 
			
		||||
                "Base.Animal.Fish.Guppy",
 | 
			
		||||
                "Base.Animal.Mammal",
 | 
			
		||||
                "Base.Animal.Mammal.Dog",
 | 
			
		||||
                "Base.Animal.Mammal.Human",
 | 
			
		||||
            ),
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            Fish._subclasses, ("Base.Animal.Fish", "Base.Animal.Fish.Guppy")
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(Guppy._subclasses, ("Base.Animal.Fish.Guppy",))
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            Mammal._subclasses,
 | 
			
		||||
            (
 | 
			
		||||
                "Base.Animal.Mammal",
 | 
			
		||||
                "Base.Animal.Mammal.Dog",
 | 
			
		||||
                "Base.Animal.Mammal.Human",
 | 
			
		||||
            ),
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(Human._subclasses, ("Base.Animal.Mammal.Human",))
 | 
			
		||||
 | 
			
		||||
    def test_dynamic_declarations(self):
 | 
			
		||||
        """Test that declaring an extra class updates meta data"""
 | 
			
		||||
 | 
			
		||||
        class Animal(Document):
 | 
			
		||||
            meta = {'allow_inheritance': True}
 | 
			
		||||
            meta = {"allow_inheritance": True}
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(Animal._superclasses, ())
 | 
			
		||||
        self.assertEqual(Animal._subclasses, ('Animal',))
 | 
			
		||||
        self.assertEqual(Animal._subclasses, ("Animal",))
 | 
			
		||||
 | 
			
		||||
        # Test dynamically adding a class changes the meta data
 | 
			
		||||
        class Fish(Animal):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(Animal._superclasses, ())
 | 
			
		||||
        self.assertEqual(Animal._subclasses, ('Animal', 'Animal.Fish'))
 | 
			
		||||
        self.assertEqual(Animal._subclasses, ("Animal", "Animal.Fish"))
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(Fish._superclasses, ('Animal', ))
 | 
			
		||||
        self.assertEqual(Fish._subclasses, ('Animal.Fish',))
 | 
			
		||||
        self.assertEqual(Fish._superclasses, ("Animal",))
 | 
			
		||||
        self.assertEqual(Fish._subclasses, ("Animal.Fish",))
 | 
			
		||||
 | 
			
		||||
        # Test dynamically adding an inherited class changes the meta data
 | 
			
		||||
        class Pike(Fish):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(Animal._superclasses, ())
 | 
			
		||||
        self.assertEqual(Animal._subclasses, ('Animal', 'Animal.Fish',
 | 
			
		||||
                                              'Animal.Fish.Pike'))
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            Animal._subclasses, ("Animal", "Animal.Fish", "Animal.Fish.Pike")
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(Fish._superclasses, ('Animal', ))
 | 
			
		||||
        self.assertEqual(Fish._subclasses, ('Animal.Fish', 'Animal.Fish.Pike'))
 | 
			
		||||
        self.assertEqual(Fish._superclasses, ("Animal",))
 | 
			
		||||
        self.assertEqual(Fish._subclasses, ("Animal.Fish", "Animal.Fish.Pike"))
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(Pike._superclasses, ('Animal', 'Animal.Fish'))
 | 
			
		||||
        self.assertEqual(Pike._subclasses, ('Animal.Fish.Pike',))
 | 
			
		||||
        self.assertEqual(Pike._superclasses, ("Animal", "Animal.Fish"))
 | 
			
		||||
        self.assertEqual(Pike._subclasses, ("Animal.Fish.Pike",))
 | 
			
		||||
 | 
			
		||||
    def test_inheritance_meta_data(self):
 | 
			
		||||
        """Ensure that document may inherit fields from a superclass document.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            age = IntField()
 | 
			
		||||
 | 
			
		||||
            meta = {'allow_inheritance': True}
 | 
			
		||||
            meta = {"allow_inheritance": True}
 | 
			
		||||
 | 
			
		||||
        class Employee(Person):
 | 
			
		||||
            salary = IntField()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(['_cls', 'age', 'id', 'name', 'salary'],
 | 
			
		||||
                         sorted(Employee._fields.keys()))
 | 
			
		||||
        self.assertEqual(Employee._get_collection_name(),
 | 
			
		||||
                         Person._get_collection_name())
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            ["_cls", "age", "id", "name", "salary"], sorted(Employee._fields.keys())
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(Employee._get_collection_name(), Person._get_collection_name())
 | 
			
		||||
 | 
			
		||||
    def test_inheritance_to_mongo_keys(self):
 | 
			
		||||
        """Ensure that document may inherit fields from a superclass document.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            age = IntField()
 | 
			
		||||
 | 
			
		||||
            meta = {'allow_inheritance': True}
 | 
			
		||||
            meta = {"allow_inheritance": True}
 | 
			
		||||
 | 
			
		||||
        class Employee(Person):
 | 
			
		||||
            salary = IntField()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(['_cls', 'age', 'id', 'name', 'salary'],
 | 
			
		||||
                         sorted(Employee._fields.keys()))
 | 
			
		||||
        self.assertEqual(Person(name="Bob", age=35).to_mongo().keys(),
 | 
			
		||||
                         ['_cls', 'name', 'age'])
 | 
			
		||||
        self.assertEqual(Employee(name="Bob", age=35, salary=0).to_mongo().keys(),
 | 
			
		||||
                         ['_cls', 'name', 'age', 'salary'])
 | 
			
		||||
        self.assertEqual(Employee._get_collection_name(),
 | 
			
		||||
                         Person._get_collection_name())
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            ["_cls", "age", "id", "name", "salary"], sorted(Employee._fields.keys())
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            Person(name="Bob", age=35).to_mongo().keys(), ["_cls", "name", "age"]
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            Employee(name="Bob", age=35, salary=0).to_mongo().keys(),
 | 
			
		||||
            ["_cls", "name", "age", "salary"],
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(Employee._get_collection_name(), Person._get_collection_name())
 | 
			
		||||
 | 
			
		||||
    def test_indexes_and_multiple_inheritance(self):
 | 
			
		||||
        """ Ensure that all of the indexes are created for a document with
 | 
			
		||||
@@ -210,18 +287,12 @@ class InheritanceTest(MongoDBTestCase):
 | 
			
		||||
        class A(Document):
 | 
			
		||||
            a = StringField()
 | 
			
		||||
 | 
			
		||||
            meta = {
 | 
			
		||||
                'allow_inheritance': True,
 | 
			
		||||
                'indexes': ['a']
 | 
			
		||||
            }
 | 
			
		||||
            meta = {"allow_inheritance": True, "indexes": ["a"]}
 | 
			
		||||
 | 
			
		||||
        class B(Document):
 | 
			
		||||
            b = StringField()
 | 
			
		||||
 | 
			
		||||
            meta = {
 | 
			
		||||
                'allow_inheritance': True,
 | 
			
		||||
                'indexes': ['b']
 | 
			
		||||
            }
 | 
			
		||||
            meta = {"allow_inheritance": True, "indexes": ["b"]}
 | 
			
		||||
 | 
			
		||||
        class C(A, B):
 | 
			
		||||
            pass
 | 
			
		||||
@@ -233,8 +304,12 @@ class InheritanceTest(MongoDBTestCase):
 | 
			
		||||
        C.ensure_indexes()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            sorted([idx['key'] for idx in C._get_collection().index_information().values()]),
 | 
			
		||||
            sorted([[(u'_cls', 1), (u'b', 1)], [(u'_id', 1)], [(u'_cls', 1), (u'a', 1)]])
 | 
			
		||||
            sorted(
 | 
			
		||||
                [idx["key"] for idx in C._get_collection().index_information().values()]
 | 
			
		||||
            ),
 | 
			
		||||
            sorted(
 | 
			
		||||
                [[(u"_cls", 1), (u"b", 1)], [(u"_id", 1)], [(u"_cls", 1), (u"a", 1)]]
 | 
			
		||||
            ),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_polymorphic_queries(self):
 | 
			
		||||
@@ -242,11 +317,19 @@ class InheritanceTest(MongoDBTestCase):
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Animal(Document):
 | 
			
		||||
            meta = {'allow_inheritance': True}
 | 
			
		||||
        class Fish(Animal): pass
 | 
			
		||||
        class Mammal(Animal): pass
 | 
			
		||||
        class Dog(Mammal): pass
 | 
			
		||||
        class Human(Mammal): pass
 | 
			
		||||
            meta = {"allow_inheritance": True}
 | 
			
		||||
 | 
			
		||||
        class Fish(Animal):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        class Mammal(Animal):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        class Dog(Mammal):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        class Human(Mammal):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        Animal.drop_collection()
 | 
			
		||||
 | 
			
		||||
@@ -269,58 +352,68 @@ class InheritanceTest(MongoDBTestCase):
 | 
			
		||||
        """Ensure that inheritance is disabled by default on simple
 | 
			
		||||
        classes and that _cls will not be used.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Animal(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
        # can't inherit because Animal didn't explicitly allow inheritance
 | 
			
		||||
        with self.assertRaises(ValueError) as cm:
 | 
			
		||||
 | 
			
		||||
            class Dog(Animal):
 | 
			
		||||
                pass
 | 
			
		||||
 | 
			
		||||
        self.assertIn("Document Animal may not be subclassed", str(cm.exception))
 | 
			
		||||
 | 
			
		||||
        # Check that _cls etc aren't present on simple documents
 | 
			
		||||
        dog = Animal(name='dog').save()
 | 
			
		||||
        self.assertEqual(dog.to_mongo().keys(), ['_id', 'name'])
 | 
			
		||||
        dog = Animal(name="dog").save()
 | 
			
		||||
        self.assertEqual(dog.to_mongo().keys(), ["_id", "name"])
 | 
			
		||||
 | 
			
		||||
        collection = self.db[Animal._get_collection_name()]
 | 
			
		||||
        obj = collection.find_one()
 | 
			
		||||
        self.assertNotIn('_cls', obj)
 | 
			
		||||
        self.assertNotIn("_cls", obj)
 | 
			
		||||
 | 
			
		||||
    def test_cant_turn_off_inheritance_on_subclass(self):
 | 
			
		||||
        """Ensure if inheritance is on in a subclass you cant turn it off.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Animal(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            meta = {'allow_inheritance': True}
 | 
			
		||||
            meta = {"allow_inheritance": True}
 | 
			
		||||
 | 
			
		||||
        with self.assertRaises(ValueError) as cm:
 | 
			
		||||
 | 
			
		||||
            class Mammal(Animal):
 | 
			
		||||
                meta = {'allow_inheritance': False}
 | 
			
		||||
        self.assertEqual(str(cm.exception), 'Only direct subclasses of Document may set "allow_inheritance" to False')
 | 
			
		||||
                meta = {"allow_inheritance": False}
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            str(cm.exception),
 | 
			
		||||
            'Only direct subclasses of Document may set "allow_inheritance" to False',
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_allow_inheritance_abstract_document(self):
 | 
			
		||||
        """Ensure that abstract documents can set inheritance rules and that
 | 
			
		||||
        _cls will not be used.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class FinalDocument(Document):
 | 
			
		||||
            meta = {'abstract': True,
 | 
			
		||||
                    'allow_inheritance': False}
 | 
			
		||||
            meta = {"abstract": True, "allow_inheritance": False}
 | 
			
		||||
 | 
			
		||||
        class Animal(FinalDocument):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
        with self.assertRaises(ValueError) as cm:
 | 
			
		||||
 | 
			
		||||
            class Mammal(Animal):
 | 
			
		||||
                pass
 | 
			
		||||
 | 
			
		||||
        # Check that _cls isn't present in simple documents
 | 
			
		||||
        doc = Animal(name='dog')
 | 
			
		||||
        self.assertNotIn('_cls', doc.to_mongo())
 | 
			
		||||
        doc = Animal(name="dog")
 | 
			
		||||
        self.assertNotIn("_cls", doc.to_mongo())
 | 
			
		||||
 | 
			
		||||
    def test_using_abstract_class_in_reference_field(self):
 | 
			
		||||
        # Ensures no regression of #1920
 | 
			
		||||
        class AbstractHuman(Document):
 | 
			
		||||
            meta = {'abstract': True}
 | 
			
		||||
            meta = {"abstract": True}
 | 
			
		||||
 | 
			
		||||
        class Dad(AbstractHuman):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
@@ -329,130 +422,122 @@ class InheritanceTest(MongoDBTestCase):
 | 
			
		||||
            dad = ReferenceField(AbstractHuman)  # Referencing the abstract class
 | 
			
		||||
            address = StringField()
 | 
			
		||||
 | 
			
		||||
        dad = Dad(name='5').save()
 | 
			
		||||
        Home(dad=dad, address='street').save()
 | 
			
		||||
        dad = Dad(name="5").save()
 | 
			
		||||
        Home(dad=dad, address="street").save()
 | 
			
		||||
 | 
			
		||||
        home = Home.objects.first()
 | 
			
		||||
        home.address = 'garbage'
 | 
			
		||||
        home.save()     # Was failing with ValidationError
 | 
			
		||||
        home.address = "garbage"
 | 
			
		||||
        home.save()  # Was failing with ValidationError
 | 
			
		||||
 | 
			
		||||
    def test_abstract_class_referencing_self(self):
 | 
			
		||||
        # Ensures no regression of #1920
 | 
			
		||||
        class Human(Document):
 | 
			
		||||
            meta = {'abstract': True}
 | 
			
		||||
            creator = ReferenceField('self', dbref=True)
 | 
			
		||||
            meta = {"abstract": True}
 | 
			
		||||
            creator = ReferenceField("self", dbref=True)
 | 
			
		||||
 | 
			
		||||
        class User(Human):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
        user = User(name='John').save()
 | 
			
		||||
        user2 = User(name='Foo', creator=user).save()
 | 
			
		||||
        user = User(name="John").save()
 | 
			
		||||
        user2 = User(name="Foo", creator=user).save()
 | 
			
		||||
 | 
			
		||||
        user2 = User.objects.with_id(user2.id)
 | 
			
		||||
        user2.name = 'Bar'
 | 
			
		||||
        user2.save()    # Was failing with ValidationError
 | 
			
		||||
        user2.name = "Bar"
 | 
			
		||||
        user2.save()  # Was failing with ValidationError
 | 
			
		||||
 | 
			
		||||
    def test_abstract_handle_ids_in_metaclass_properly(self):
 | 
			
		||||
 | 
			
		||||
        class City(Document):
 | 
			
		||||
            continent = StringField()
 | 
			
		||||
            meta = {'abstract': True,
 | 
			
		||||
                    'allow_inheritance': False}
 | 
			
		||||
            meta = {"abstract": True, "allow_inheritance": False}
 | 
			
		||||
 | 
			
		||||
        class EuropeanCity(City):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
        berlin = EuropeanCity(name='Berlin', continent='Europe')
 | 
			
		||||
        berlin = EuropeanCity(name="Berlin", continent="Europe")
 | 
			
		||||
        self.assertEqual(len(berlin._db_field_map), len(berlin._fields_ordered))
 | 
			
		||||
        self.assertEqual(len(berlin._reverse_db_field_map), len(berlin._fields_ordered))
 | 
			
		||||
        self.assertEqual(len(berlin._fields_ordered), 3)
 | 
			
		||||
        self.assertEqual(berlin._fields_ordered[0], 'id')
 | 
			
		||||
        self.assertEqual(berlin._fields_ordered[0], "id")
 | 
			
		||||
 | 
			
		||||
    def test_auto_id_not_set_if_specific_in_parent_class(self):
 | 
			
		||||
 | 
			
		||||
        class City(Document):
 | 
			
		||||
            continent = StringField()
 | 
			
		||||
            city_id = IntField(primary_key=True)
 | 
			
		||||
            meta = {'abstract': True,
 | 
			
		||||
                    'allow_inheritance': False}
 | 
			
		||||
            meta = {"abstract": True, "allow_inheritance": False}
 | 
			
		||||
 | 
			
		||||
        class EuropeanCity(City):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
        berlin = EuropeanCity(name='Berlin', continent='Europe')
 | 
			
		||||
        berlin = EuropeanCity(name="Berlin", continent="Europe")
 | 
			
		||||
        self.assertEqual(len(berlin._db_field_map), len(berlin._fields_ordered))
 | 
			
		||||
        self.assertEqual(len(berlin._reverse_db_field_map), len(berlin._fields_ordered))
 | 
			
		||||
        self.assertEqual(len(berlin._fields_ordered), 3)
 | 
			
		||||
        self.assertEqual(berlin._fields_ordered[0], 'city_id')
 | 
			
		||||
        self.assertEqual(berlin._fields_ordered[0], "city_id")
 | 
			
		||||
 | 
			
		||||
    def test_auto_id_vs_non_pk_id_field(self):
 | 
			
		||||
 | 
			
		||||
        class City(Document):
 | 
			
		||||
            continent = StringField()
 | 
			
		||||
            id = IntField()
 | 
			
		||||
            meta = {'abstract': True,
 | 
			
		||||
                    'allow_inheritance': False}
 | 
			
		||||
            meta = {"abstract": True, "allow_inheritance": False}
 | 
			
		||||
 | 
			
		||||
        class EuropeanCity(City):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
        berlin = EuropeanCity(name='Berlin', continent='Europe')
 | 
			
		||||
        berlin = EuropeanCity(name="Berlin", continent="Europe")
 | 
			
		||||
        self.assertEqual(len(berlin._db_field_map), len(berlin._fields_ordered))
 | 
			
		||||
        self.assertEqual(len(berlin._reverse_db_field_map), len(berlin._fields_ordered))
 | 
			
		||||
        self.assertEqual(len(berlin._fields_ordered), 4)
 | 
			
		||||
        self.assertEqual(berlin._fields_ordered[0], 'auto_id_0')
 | 
			
		||||
        self.assertEqual(berlin._fields_ordered[0], "auto_id_0")
 | 
			
		||||
        berlin.save()
 | 
			
		||||
        self.assertEqual(berlin.pk, berlin.auto_id_0)
 | 
			
		||||
 | 
			
		||||
    def test_abstract_document_creation_does_not_fail(self):
 | 
			
		||||
        class City(Document):
 | 
			
		||||
            continent = StringField()
 | 
			
		||||
            meta = {'abstract': True,
 | 
			
		||||
                    'allow_inheritance': False}
 | 
			
		||||
            meta = {"abstract": True, "allow_inheritance": False}
 | 
			
		||||
 | 
			
		||||
        city = City(continent='asia')
 | 
			
		||||
        city = City(continent="asia")
 | 
			
		||||
        self.assertEqual(None, city.pk)
 | 
			
		||||
        # TODO: expected error? Shouldn't we create a new error type?
 | 
			
		||||
        with self.assertRaises(KeyError):
 | 
			
		||||
            setattr(city, 'pk', 1)
 | 
			
		||||
            setattr(city, "pk", 1)
 | 
			
		||||
 | 
			
		||||
    def test_allow_inheritance_embedded_document(self):
 | 
			
		||||
        """Ensure embedded documents respect inheritance."""
 | 
			
		||||
 | 
			
		||||
        class Comment(EmbeddedDocument):
 | 
			
		||||
            content = StringField()
 | 
			
		||||
 | 
			
		||||
        with self.assertRaises(ValueError):
 | 
			
		||||
 | 
			
		||||
            class SpecialComment(Comment):
 | 
			
		||||
                pass
 | 
			
		||||
 | 
			
		||||
        doc = Comment(content='test')
 | 
			
		||||
        self.assertNotIn('_cls', doc.to_mongo())
 | 
			
		||||
        doc = Comment(content="test")
 | 
			
		||||
        self.assertNotIn("_cls", doc.to_mongo())
 | 
			
		||||
 | 
			
		||||
        class Comment(EmbeddedDocument):
 | 
			
		||||
            content = StringField()
 | 
			
		||||
            meta = {'allow_inheritance': True}
 | 
			
		||||
            meta = {"allow_inheritance": True}
 | 
			
		||||
 | 
			
		||||
        doc = Comment(content='test')
 | 
			
		||||
        self.assertIn('_cls', doc.to_mongo())
 | 
			
		||||
        doc = Comment(content="test")
 | 
			
		||||
        self.assertIn("_cls", doc.to_mongo())
 | 
			
		||||
 | 
			
		||||
    def test_document_inheritance(self):
 | 
			
		||||
        """Ensure mutliple inheritance of abstract documents
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class DateCreatedDocument(Document):
 | 
			
		||||
            meta = {
 | 
			
		||||
                'allow_inheritance': True,
 | 
			
		||||
                'abstract': True,
 | 
			
		||||
            }
 | 
			
		||||
            meta = {"allow_inheritance": True, "abstract": True}
 | 
			
		||||
 | 
			
		||||
        class DateUpdatedDocument(Document):
 | 
			
		||||
            meta = {
 | 
			
		||||
                'allow_inheritance': True,
 | 
			
		||||
                'abstract': True,
 | 
			
		||||
            }
 | 
			
		||||
            meta = {"allow_inheritance": True, "abstract": True}
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
 | 
			
		||||
            class MyDocument(DateCreatedDocument, DateUpdatedDocument):
 | 
			
		||||
                pass
 | 
			
		||||
 | 
			
		||||
        except Exception:
 | 
			
		||||
            self.assertTrue(False, "Couldn't create MyDocument class")
 | 
			
		||||
 | 
			
		||||
@@ -460,47 +545,55 @@ class InheritanceTest(MongoDBTestCase):
 | 
			
		||||
        """Ensure that a document superclass can be marked as abstract
 | 
			
		||||
        thereby not using it as the name for the collection."""
 | 
			
		||||
 | 
			
		||||
        defaults = {'index_background': True,
 | 
			
		||||
                    'index_drop_dups': True,
 | 
			
		||||
                    'index_opts': {'hello': 'world'},
 | 
			
		||||
                    'allow_inheritance': True,
 | 
			
		||||
                    'queryset_class': 'QuerySet',
 | 
			
		||||
                    'db_alias': 'myDB',
 | 
			
		||||
                    'shard_key': ('hello', 'world')}
 | 
			
		||||
        defaults = {
 | 
			
		||||
            "index_background": True,
 | 
			
		||||
            "index_drop_dups": True,
 | 
			
		||||
            "index_opts": {"hello": "world"},
 | 
			
		||||
            "allow_inheritance": True,
 | 
			
		||||
            "queryset_class": "QuerySet",
 | 
			
		||||
            "db_alias": "myDB",
 | 
			
		||||
            "shard_key": ("hello", "world"),
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        meta_settings = {'abstract': True}
 | 
			
		||||
        meta_settings = {"abstract": True}
 | 
			
		||||
        meta_settings.update(defaults)
 | 
			
		||||
 | 
			
		||||
        class Animal(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            meta = meta_settings
 | 
			
		||||
 | 
			
		||||
        class Fish(Animal): pass
 | 
			
		||||
        class Guppy(Fish): pass
 | 
			
		||||
        class Fish(Animal):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        class Guppy(Fish):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        class Mammal(Animal):
 | 
			
		||||
            meta = {'abstract': True}
 | 
			
		||||
        class Human(Mammal): pass
 | 
			
		||||
            meta = {"abstract": True}
 | 
			
		||||
 | 
			
		||||
        class Human(Mammal):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        for k, v in iteritems(defaults):
 | 
			
		||||
            for cls in [Animal, Fish, Guppy]:
 | 
			
		||||
                self.assertEqual(cls._meta[k], v)
 | 
			
		||||
 | 
			
		||||
        self.assertNotIn('collection', Animal._meta)
 | 
			
		||||
        self.assertNotIn('collection', Mammal._meta)
 | 
			
		||||
        self.assertNotIn("collection", Animal._meta)
 | 
			
		||||
        self.assertNotIn("collection", Mammal._meta)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(Animal._get_collection_name(), None)
 | 
			
		||||
        self.assertEqual(Mammal._get_collection_name(), None)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(Fish._get_collection_name(), 'fish')
 | 
			
		||||
        self.assertEqual(Guppy._get_collection_name(), 'fish')
 | 
			
		||||
        self.assertEqual(Human._get_collection_name(), 'human')
 | 
			
		||||
        self.assertEqual(Fish._get_collection_name(), "fish")
 | 
			
		||||
        self.assertEqual(Guppy._get_collection_name(), "fish")
 | 
			
		||||
        self.assertEqual(Human._get_collection_name(), "human")
 | 
			
		||||
 | 
			
		||||
        # ensure that a subclass of a non-abstract class can't be abstract
 | 
			
		||||
        with self.assertRaises(ValueError):
 | 
			
		||||
 | 
			
		||||
            class EvilHuman(Human):
 | 
			
		||||
                evil = BooleanField(default=True)
 | 
			
		||||
                meta = {'abstract': True}
 | 
			
		||||
                meta = {"abstract": True}
 | 
			
		||||
 | 
			
		||||
    def test_abstract_embedded_documents(self):
 | 
			
		||||
        # 789: EmbeddedDocument shouldn't inherit abstract
 | 
			
		||||
@@ -519,7 +612,7 @@ class InheritanceTest(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        class Drink(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            meta = {'allow_inheritance': True}
 | 
			
		||||
            meta = {"allow_inheritance": True}
 | 
			
		||||
 | 
			
		||||
        class Drinker(Document):
 | 
			
		||||
            drink = GenericReferenceField()
 | 
			
		||||
@@ -528,13 +621,13 @@ class InheritanceTest(MongoDBTestCase):
 | 
			
		||||
            warnings.simplefilter("error")
 | 
			
		||||
 | 
			
		||||
            class AcloholicDrink(Drink):
 | 
			
		||||
                meta = {'collection': 'booze'}
 | 
			
		||||
                meta = {"collection": "booze"}
 | 
			
		||||
 | 
			
		||||
        except SyntaxWarning:
 | 
			
		||||
            warnings.simplefilter("ignore")
 | 
			
		||||
 | 
			
		||||
            class AlcoholicDrink(Drink):
 | 
			
		||||
                meta = {'collection': 'booze'}
 | 
			
		||||
                meta = {"collection": "booze"}
 | 
			
		||||
 | 
			
		||||
        else:
 | 
			
		||||
            raise AssertionError("SyntaxWarning should be triggered")
 | 
			
		||||
@@ -545,13 +638,13 @@ class InheritanceTest(MongoDBTestCase):
 | 
			
		||||
        AlcoholicDrink.drop_collection()
 | 
			
		||||
        Drinker.drop_collection()
 | 
			
		||||
 | 
			
		||||
        red_bull = Drink(name='Red Bull')
 | 
			
		||||
        red_bull = Drink(name="Red Bull")
 | 
			
		||||
        red_bull.save()
 | 
			
		||||
 | 
			
		||||
        programmer = Drinker(drink=red_bull)
 | 
			
		||||
        programmer.save()
 | 
			
		||||
 | 
			
		||||
        beer = AlcoholicDrink(name='Beer')
 | 
			
		||||
        beer = AlcoholicDrink(name="Beer")
 | 
			
		||||
        beer.save()
 | 
			
		||||
        real_person = Drinker(drink=beer)
 | 
			
		||||
        real_person.save()
 | 
			
		||||
@@ -560,5 +653,5 @@ class InheritanceTest(MongoDBTestCase):
 | 
			
		||||
        self.assertEqual(Drinker.objects[1].drink.name, beer.name)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    unittest.main()
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -13,9 +13,8 @@ __all__ = ("TestJson",)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestJson(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        connect(db='mongoenginetest')
 | 
			
		||||
        connect(db="mongoenginetest")
 | 
			
		||||
 | 
			
		||||
    def test_json_names(self):
 | 
			
		||||
        """
 | 
			
		||||
@@ -25,22 +24,24 @@ class TestJson(unittest.TestCase):
 | 
			
		||||
        a to_json with the original class names and not the abreviated
 | 
			
		||||
        mongodb document keys
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Embedded(EmbeddedDocument):
 | 
			
		||||
            string = StringField(db_field='s')
 | 
			
		||||
            string = StringField(db_field="s")
 | 
			
		||||
 | 
			
		||||
        class Doc(Document):
 | 
			
		||||
            string = StringField(db_field='s')
 | 
			
		||||
            embedded = EmbeddedDocumentField(Embedded, db_field='e')
 | 
			
		||||
            string = StringField(db_field="s")
 | 
			
		||||
            embedded = EmbeddedDocumentField(Embedded, db_field="e")
 | 
			
		||||
 | 
			
		||||
        doc = Doc(string="Hello", embedded=Embedded(string="Inner Hello"))
 | 
			
		||||
        doc_json = doc.to_json(sort_keys=True, use_db_field=False, separators=(',', ':'))
 | 
			
		||||
        doc_json = doc.to_json(
 | 
			
		||||
            sort_keys=True, use_db_field=False, separators=(",", ":")
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        expected_json = """{"embedded":{"string":"Inner Hello"},"string":"Hello"}"""
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(doc_json, expected_json)
 | 
			
		||||
 | 
			
		||||
    def test_json_simple(self):
 | 
			
		||||
 | 
			
		||||
        class Embedded(EmbeddedDocument):
 | 
			
		||||
            string = StringField()
 | 
			
		||||
 | 
			
		||||
@@ -49,12 +50,14 @@ class TestJson(unittest.TestCase):
 | 
			
		||||
            embedded_field = EmbeddedDocumentField(Embedded)
 | 
			
		||||
 | 
			
		||||
            def __eq__(self, other):
 | 
			
		||||
                return (self.string == other.string and
 | 
			
		||||
                        self.embedded_field == other.embedded_field)
 | 
			
		||||
                return (
 | 
			
		||||
                    self.string == other.string
 | 
			
		||||
                    and self.embedded_field == other.embedded_field
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
        doc = Doc(string="Hi", embedded_field=Embedded(string="Hi"))
 | 
			
		||||
 | 
			
		||||
        doc_json = doc.to_json(sort_keys=True, separators=(',', ':'))
 | 
			
		||||
        doc_json = doc.to_json(sort_keys=True, separators=(",", ":"))
 | 
			
		||||
        expected_json = """{"embedded_field":{"string":"Hi"},"string":"Hi"}"""
 | 
			
		||||
        self.assertEqual(doc_json, expected_json)
 | 
			
		||||
 | 
			
		||||
@@ -68,41 +71,43 @@ class TestJson(unittest.TestCase):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        class Doc(Document):
 | 
			
		||||
            string_field = StringField(default='1')
 | 
			
		||||
            string_field = StringField(default="1")
 | 
			
		||||
            int_field = IntField(default=1)
 | 
			
		||||
            float_field = FloatField(default=1.1)
 | 
			
		||||
            boolean_field = BooleanField(default=True)
 | 
			
		||||
            datetime_field = DateTimeField(default=datetime.now)
 | 
			
		||||
            embedded_document_field = EmbeddedDocumentField(EmbeddedDoc,
 | 
			
		||||
                                        default=lambda: EmbeddedDoc())
 | 
			
		||||
            embedded_document_field = EmbeddedDocumentField(
 | 
			
		||||
                EmbeddedDoc, default=lambda: EmbeddedDoc()
 | 
			
		||||
            )
 | 
			
		||||
            list_field = ListField(default=lambda: [1, 2, 3])
 | 
			
		||||
            dict_field = DictField(default=lambda: {"hello": "world"})
 | 
			
		||||
            objectid_field = ObjectIdField(default=ObjectId)
 | 
			
		||||
            reference_field = ReferenceField(Simple, default=lambda:
 | 
			
		||||
                                                        Simple().save())
 | 
			
		||||
            reference_field = ReferenceField(Simple, default=lambda: Simple().save())
 | 
			
		||||
            map_field = MapField(IntField(), default=lambda: {"simple": 1})
 | 
			
		||||
            decimal_field = DecimalField(default=1.0)
 | 
			
		||||
            complex_datetime_field = ComplexDateTimeField(default=datetime.now)
 | 
			
		||||
            url_field = URLField(default="http://mongoengine.org")
 | 
			
		||||
            dynamic_field = DynamicField(default=1)
 | 
			
		||||
            generic_reference_field = GenericReferenceField(
 | 
			
		||||
                                            default=lambda: Simple().save())
 | 
			
		||||
            sorted_list_field = SortedListField(IntField(),
 | 
			
		||||
                                                default=lambda: [1, 2, 3])
 | 
			
		||||
                default=lambda: Simple().save()
 | 
			
		||||
            )
 | 
			
		||||
            sorted_list_field = SortedListField(IntField(), default=lambda: [1, 2, 3])
 | 
			
		||||
            email_field = EmailField(default="ross@example.com")
 | 
			
		||||
            geo_point_field = GeoPointField(default=lambda: [1, 2])
 | 
			
		||||
            sequence_field = SequenceField()
 | 
			
		||||
            uuid_field = UUIDField(default=uuid.uuid4)
 | 
			
		||||
            generic_embedded_document_field = GenericEmbeddedDocumentField(
 | 
			
		||||
                                        default=lambda: EmbeddedDoc())
 | 
			
		||||
                default=lambda: EmbeddedDoc()
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            def __eq__(self, other):
 | 
			
		||||
                import json
 | 
			
		||||
 | 
			
		||||
                return json.loads(self.to_json()) == json.loads(other.to_json())
 | 
			
		||||
 | 
			
		||||
        doc = Doc()
 | 
			
		||||
        self.assertEqual(doc, Doc.from_json(doc.to_json()))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    unittest.main()
 | 
			
		||||
 
 | 
			
		||||
@@ -8,49 +8,56 @@ __all__ = ("ValidatorErrorTest",)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ValidatorErrorTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        connect(db='mongoenginetest')
 | 
			
		||||
        connect(db="mongoenginetest")
 | 
			
		||||
 | 
			
		||||
    def test_to_dict(self):
 | 
			
		||||
        """Ensure a ValidationError handles error to_dict correctly.
 | 
			
		||||
        """
 | 
			
		||||
        error = ValidationError('root')
 | 
			
		||||
        error = ValidationError("root")
 | 
			
		||||
        self.assertEqual(error.to_dict(), {})
 | 
			
		||||
 | 
			
		||||
        # 1st level error schema
 | 
			
		||||
        error.errors = {'1st': ValidationError('bad 1st'), }
 | 
			
		||||
        self.assertIn('1st', error.to_dict())
 | 
			
		||||
        self.assertEqual(error.to_dict()['1st'], 'bad 1st')
 | 
			
		||||
        error.errors = {"1st": ValidationError("bad 1st")}
 | 
			
		||||
        self.assertIn("1st", error.to_dict())
 | 
			
		||||
        self.assertEqual(error.to_dict()["1st"], "bad 1st")
 | 
			
		||||
 | 
			
		||||
        # 2nd level error schema
 | 
			
		||||
        error.errors = {'1st': ValidationError('bad 1st', errors={
 | 
			
		||||
            '2nd': ValidationError('bad 2nd'),
 | 
			
		||||
        })}
 | 
			
		||||
        self.assertIn('1st', error.to_dict())
 | 
			
		||||
        self.assertIsInstance(error.to_dict()['1st'], dict)
 | 
			
		||||
        self.assertIn('2nd', error.to_dict()['1st'])
 | 
			
		||||
        self.assertEqual(error.to_dict()['1st']['2nd'], 'bad 2nd')
 | 
			
		||||
        error.errors = {
 | 
			
		||||
            "1st": ValidationError(
 | 
			
		||||
                "bad 1st", errors={"2nd": ValidationError("bad 2nd")}
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
        self.assertIn("1st", error.to_dict())
 | 
			
		||||
        self.assertIsInstance(error.to_dict()["1st"], dict)
 | 
			
		||||
        self.assertIn("2nd", error.to_dict()["1st"])
 | 
			
		||||
        self.assertEqual(error.to_dict()["1st"]["2nd"], "bad 2nd")
 | 
			
		||||
 | 
			
		||||
        # moar levels
 | 
			
		||||
        error.errors = {'1st': ValidationError('bad 1st', errors={
 | 
			
		||||
            '2nd': ValidationError('bad 2nd', errors={
 | 
			
		||||
                '3rd': ValidationError('bad 3rd', errors={
 | 
			
		||||
                    '4th': ValidationError('Inception'),
 | 
			
		||||
                }),
 | 
			
		||||
            }),
 | 
			
		||||
        })}
 | 
			
		||||
        self.assertIn('1st', error.to_dict())
 | 
			
		||||
        self.assertIn('2nd', error.to_dict()['1st'])
 | 
			
		||||
        self.assertIn('3rd', error.to_dict()['1st']['2nd'])
 | 
			
		||||
        self.assertIn('4th', error.to_dict()['1st']['2nd']['3rd'])
 | 
			
		||||
        self.assertEqual(error.to_dict()['1st']['2nd']['3rd']['4th'],
 | 
			
		||||
                         'Inception')
 | 
			
		||||
        error.errors = {
 | 
			
		||||
            "1st": ValidationError(
 | 
			
		||||
                "bad 1st",
 | 
			
		||||
                errors={
 | 
			
		||||
                    "2nd": ValidationError(
 | 
			
		||||
                        "bad 2nd",
 | 
			
		||||
                        errors={
 | 
			
		||||
                            "3rd": ValidationError(
 | 
			
		||||
                                "bad 3rd", errors={"4th": ValidationError("Inception")}
 | 
			
		||||
                            )
 | 
			
		||||
                        },
 | 
			
		||||
                    )
 | 
			
		||||
                },
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
        self.assertIn("1st", error.to_dict())
 | 
			
		||||
        self.assertIn("2nd", error.to_dict()["1st"])
 | 
			
		||||
        self.assertIn("3rd", error.to_dict()["1st"]["2nd"])
 | 
			
		||||
        self.assertIn("4th", error.to_dict()["1st"]["2nd"]["3rd"])
 | 
			
		||||
        self.assertEqual(error.to_dict()["1st"]["2nd"]["3rd"]["4th"], "Inception")
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(error.message, "root(2nd.3rd.4th.Inception: ['1st'])")
 | 
			
		||||
 | 
			
		||||
    def test_model_validation(self):
 | 
			
		||||
 | 
			
		||||
        class User(Document):
 | 
			
		||||
            username = StringField(primary_key=True)
 | 
			
		||||
            name = StringField(required=True)
 | 
			
		||||
@@ -59,9 +66,10 @@ class ValidatorErrorTest(unittest.TestCase):
 | 
			
		||||
            User().validate()
 | 
			
		||||
        except ValidationError as e:
 | 
			
		||||
            self.assertIn("User:None", e.message)
 | 
			
		||||
            self.assertEqual(e.to_dict(), {
 | 
			
		||||
                'username': 'Field is required',
 | 
			
		||||
                'name': 'Field is required'})
 | 
			
		||||
            self.assertEqual(
 | 
			
		||||
                e.to_dict(),
 | 
			
		||||
                {"username": "Field is required", "name": "Field is required"},
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        user = User(username="RossC0", name="Ross").save()
 | 
			
		||||
        user.name = None
 | 
			
		||||
@@ -69,14 +77,13 @@ class ValidatorErrorTest(unittest.TestCase):
 | 
			
		||||
            user.save()
 | 
			
		||||
        except ValidationError as e:
 | 
			
		||||
            self.assertIn("User:RossC0", e.message)
 | 
			
		||||
            self.assertEqual(e.to_dict(), {
 | 
			
		||||
                'name': 'Field is required'})
 | 
			
		||||
            self.assertEqual(e.to_dict(), {"name": "Field is required"})
 | 
			
		||||
 | 
			
		||||
    def test_fields_rewrite(self):
 | 
			
		||||
        class BasePerson(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            age = IntField()
 | 
			
		||||
            meta = {'abstract': True}
 | 
			
		||||
            meta = {"abstract": True}
 | 
			
		||||
 | 
			
		||||
        class Person(BasePerson):
 | 
			
		||||
            name = StringField(required=True)
 | 
			
		||||
@@ -87,6 +94,7 @@ class ValidatorErrorTest(unittest.TestCase):
 | 
			
		||||
    def test_embedded_document_validation(self):
 | 
			
		||||
        """Ensure that embedded documents may be validated.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Comment(EmbeddedDocument):
 | 
			
		||||
            date = DateTimeField()
 | 
			
		||||
            content = StringField(required=True)
 | 
			
		||||
@@ -94,7 +102,7 @@ class ValidatorErrorTest(unittest.TestCase):
 | 
			
		||||
        comment = Comment()
 | 
			
		||||
        self.assertRaises(ValidationError, comment.validate)
 | 
			
		||||
 | 
			
		||||
        comment.content = 'test'
 | 
			
		||||
        comment.content = "test"
 | 
			
		||||
        comment.validate()
 | 
			
		||||
 | 
			
		||||
        comment.date = 4
 | 
			
		||||
@@ -105,20 +113,20 @@ class ValidatorErrorTest(unittest.TestCase):
 | 
			
		||||
        self.assertEqual(comment._instance, None)
 | 
			
		||||
 | 
			
		||||
    def test_embedded_db_field_validate(self):
 | 
			
		||||
 | 
			
		||||
        class SubDoc(EmbeddedDocument):
 | 
			
		||||
            val = IntField(required=True)
 | 
			
		||||
 | 
			
		||||
        class Doc(Document):
 | 
			
		||||
            id = StringField(primary_key=True)
 | 
			
		||||
            e = EmbeddedDocumentField(SubDoc, db_field='eb')
 | 
			
		||||
            e = EmbeddedDocumentField(SubDoc, db_field="eb")
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            Doc(id="bad").validate()
 | 
			
		||||
        except ValidationError as e:
 | 
			
		||||
            self.assertIn("SubDoc:None", e.message)
 | 
			
		||||
            self.assertEqual(e.to_dict(), {
 | 
			
		||||
                "e": {'val': 'OK could not be converted to int'}})
 | 
			
		||||
            self.assertEqual(
 | 
			
		||||
                e.to_dict(), {"e": {"val": "OK could not be converted to int"}}
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        Doc.drop_collection()
 | 
			
		||||
 | 
			
		||||
@@ -127,24 +135,24 @@ class ValidatorErrorTest(unittest.TestCase):
 | 
			
		||||
        doc = Doc.objects.first()
 | 
			
		||||
        keys = doc._data.keys()
 | 
			
		||||
        self.assertEqual(2, len(keys))
 | 
			
		||||
        self.assertIn('e', keys)
 | 
			
		||||
        self.assertIn('id', keys)
 | 
			
		||||
        self.assertIn("e", keys)
 | 
			
		||||
        self.assertIn("id", keys)
 | 
			
		||||
 | 
			
		||||
        doc.e.val = "OK"
 | 
			
		||||
        try:
 | 
			
		||||
            doc.save()
 | 
			
		||||
        except ValidationError as e:
 | 
			
		||||
            self.assertIn("Doc:test", e.message)
 | 
			
		||||
            self.assertEqual(e.to_dict(), {
 | 
			
		||||
                "e": {'val': 'OK could not be converted to int'}})
 | 
			
		||||
            self.assertEqual(
 | 
			
		||||
                e.to_dict(), {"e": {"val": "OK could not be converted to int"}}
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    def test_embedded_weakref(self):
 | 
			
		||||
 | 
			
		||||
        class SubDoc(EmbeddedDocument):
 | 
			
		||||
            val = IntField(required=True)
 | 
			
		||||
 | 
			
		||||
        class Doc(Document):
 | 
			
		||||
            e = EmbeddedDocumentField(SubDoc, db_field='eb')
 | 
			
		||||
            e = EmbeddedDocumentField(SubDoc, db_field="eb")
 | 
			
		||||
 | 
			
		||||
        Doc.drop_collection()
 | 
			
		||||
 | 
			
		||||
@@ -167,9 +175,10 @@ class ValidatorErrorTest(unittest.TestCase):
 | 
			
		||||
        Test to ensure a ReferenceField can store a reference to a parent
 | 
			
		||||
        class when inherited. Issue #954.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Parent(Document):
 | 
			
		||||
            meta = {'allow_inheritance': True}
 | 
			
		||||
            reference = ReferenceField('self')
 | 
			
		||||
            meta = {"allow_inheritance": True}
 | 
			
		||||
            reference = ReferenceField("self")
 | 
			
		||||
 | 
			
		||||
        class Child(Parent):
 | 
			
		||||
            pass
 | 
			
		||||
@@ -190,9 +199,10 @@ class ValidatorErrorTest(unittest.TestCase):
 | 
			
		||||
        Test to ensure a ReferenceField can store a reference to a parent
 | 
			
		||||
        class when inherited and when set via attribute. Issue #954.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Parent(Document):
 | 
			
		||||
            meta = {'allow_inheritance': True}
 | 
			
		||||
            reference = ReferenceField('self')
 | 
			
		||||
            meta = {"allow_inheritance": True}
 | 
			
		||||
            reference = ReferenceField("self")
 | 
			
		||||
 | 
			
		||||
        class Child(Parent):
 | 
			
		||||
            pass
 | 
			
		||||
@@ -210,5 +220,5 @@ class ValidatorErrorTest(unittest.TestCase):
 | 
			
		||||
            self.fail("ValidationError raised: %s" % e.message)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    unittest.main()
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -14,36 +14,37 @@ from mongoengine.python_support import StringIO
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    from PIL import Image
 | 
			
		||||
 | 
			
		||||
    HAS_PIL = True
 | 
			
		||||
except ImportError:
 | 
			
		||||
    HAS_PIL = False
 | 
			
		||||
 | 
			
		||||
from tests.utils import MongoDBTestCase
 | 
			
		||||
 | 
			
		||||
TEST_IMAGE_PATH = os.path.join(os.path.dirname(__file__), 'mongoengine.png')
 | 
			
		||||
TEST_IMAGE2_PATH = os.path.join(os.path.dirname(__file__), 'mongodb_leaf.png')
 | 
			
		||||
TEST_IMAGE_PATH = os.path.join(os.path.dirname(__file__), "mongoengine.png")
 | 
			
		||||
TEST_IMAGE2_PATH = os.path.join(os.path.dirname(__file__), "mongodb_leaf.png")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_file(path):
 | 
			
		||||
    """Use a BytesIO instead of a file to allow
 | 
			
		||||
    to have a one-liner and avoid that the file remains opened"""
 | 
			
		||||
    bytes_io = StringIO()
 | 
			
		||||
    with open(path, 'rb') as f:
 | 
			
		||||
    with open(path, "rb") as f:
 | 
			
		||||
        bytes_io.write(f.read())
 | 
			
		||||
    bytes_io.seek(0)
 | 
			
		||||
    return bytes_io
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FileTest(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
    def tearDown(self):
 | 
			
		||||
        self.db.drop_collection('fs.files')
 | 
			
		||||
        self.db.drop_collection('fs.chunks')
 | 
			
		||||
        self.db.drop_collection("fs.files")
 | 
			
		||||
        self.db.drop_collection("fs.chunks")
 | 
			
		||||
 | 
			
		||||
    def test_file_field_optional(self):
 | 
			
		||||
        # Make sure FileField is optional and not required
 | 
			
		||||
        class DemoFile(Document):
 | 
			
		||||
            the_file = FileField()
 | 
			
		||||
 | 
			
		||||
        DemoFile.objects.create()
 | 
			
		||||
 | 
			
		||||
    def test_file_fields(self):
 | 
			
		||||
@@ -55,8 +56,8 @@ class FileTest(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        PutFile.drop_collection()
 | 
			
		||||
 | 
			
		||||
        text = six.b('Hello, World!')
 | 
			
		||||
        content_type = 'text/plain'
 | 
			
		||||
        text = six.b("Hello, World!")
 | 
			
		||||
        content_type = "text/plain"
 | 
			
		||||
 | 
			
		||||
        putfile = PutFile()
 | 
			
		||||
        putfile.the_file.put(text, content_type=content_type, filename="hello")
 | 
			
		||||
@@ -64,7 +65,10 @@ class FileTest(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        result = PutFile.objects.first()
 | 
			
		||||
        self.assertEqual(putfile, result)
 | 
			
		||||
        self.assertEqual("%s" % result.the_file, "<GridFSProxy: hello (%s)>" % result.the_file.grid_id)
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            "%s" % result.the_file,
 | 
			
		||||
            "<GridFSProxy: hello (%s)>" % result.the_file.grid_id,
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(result.the_file.read(), text)
 | 
			
		||||
        self.assertEqual(result.the_file.content_type, content_type)
 | 
			
		||||
        result.the_file.delete()  # Remove file from GridFS
 | 
			
		||||
@@ -89,14 +93,15 @@ class FileTest(MongoDBTestCase):
 | 
			
		||||
    def test_file_fields_stream(self):
 | 
			
		||||
        """Ensure that file fields can be written to and their data retrieved
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class StreamFile(Document):
 | 
			
		||||
            the_file = FileField()
 | 
			
		||||
 | 
			
		||||
        StreamFile.drop_collection()
 | 
			
		||||
 | 
			
		||||
        text = six.b('Hello, World!')
 | 
			
		||||
        more_text = six.b('Foo Bar')
 | 
			
		||||
        content_type = 'text/plain'
 | 
			
		||||
        text = six.b("Hello, World!")
 | 
			
		||||
        more_text = six.b("Foo Bar")
 | 
			
		||||
        content_type = "text/plain"
 | 
			
		||||
 | 
			
		||||
        streamfile = StreamFile()
 | 
			
		||||
        streamfile.the_file.new_file(content_type=content_type)
 | 
			
		||||
@@ -124,14 +129,15 @@ class FileTest(MongoDBTestCase):
 | 
			
		||||
        """Ensure that a file field can be written to after it has been saved as
 | 
			
		||||
        None
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class StreamFile(Document):
 | 
			
		||||
            the_file = FileField()
 | 
			
		||||
 | 
			
		||||
        StreamFile.drop_collection()
 | 
			
		||||
 | 
			
		||||
        text = six.b('Hello, World!')
 | 
			
		||||
        more_text = six.b('Foo Bar')
 | 
			
		||||
        content_type = 'text/plain'
 | 
			
		||||
        text = six.b("Hello, World!")
 | 
			
		||||
        more_text = six.b("Foo Bar")
 | 
			
		||||
        content_type = "text/plain"
 | 
			
		||||
 | 
			
		||||
        streamfile = StreamFile()
 | 
			
		||||
        streamfile.save()
 | 
			
		||||
@@ -157,12 +163,11 @@ class FileTest(MongoDBTestCase):
 | 
			
		||||
        self.assertTrue(result.the_file.read() is None)
 | 
			
		||||
 | 
			
		||||
    def test_file_fields_set(self):
 | 
			
		||||
 | 
			
		||||
        class SetFile(Document):
 | 
			
		||||
            the_file = FileField()
 | 
			
		||||
 | 
			
		||||
        text = six.b('Hello, World!')
 | 
			
		||||
        more_text = six.b('Foo Bar')
 | 
			
		||||
        text = six.b("Hello, World!")
 | 
			
		||||
        more_text = six.b("Foo Bar")
 | 
			
		||||
 | 
			
		||||
        SetFile.drop_collection()
 | 
			
		||||
 | 
			
		||||
@@ -184,7 +189,6 @@ class FileTest(MongoDBTestCase):
 | 
			
		||||
        result.the_file.delete()
 | 
			
		||||
 | 
			
		||||
    def test_file_field_no_default(self):
 | 
			
		||||
 | 
			
		||||
        class GridDocument(Document):
 | 
			
		||||
            the_file = FileField()
 | 
			
		||||
 | 
			
		||||
@@ -199,7 +203,7 @@ class FileTest(MongoDBTestCase):
 | 
			
		||||
            doc_a.save()
 | 
			
		||||
 | 
			
		||||
            doc_b = GridDocument.objects.with_id(doc_a.id)
 | 
			
		||||
            doc_b.the_file.replace(f, filename='doc_b')
 | 
			
		||||
            doc_b.the_file.replace(f, filename="doc_b")
 | 
			
		||||
            doc_b.save()
 | 
			
		||||
            self.assertNotEqual(doc_b.the_file.grid_id, None)
 | 
			
		||||
 | 
			
		||||
@@ -208,13 +212,13 @@ class FileTest(MongoDBTestCase):
 | 
			
		||||
            self.assertEqual(doc_b.the_file.grid_id, doc_c.the_file.grid_id)
 | 
			
		||||
 | 
			
		||||
            # Test with default
 | 
			
		||||
            doc_d = GridDocument(the_file=six.b(''))
 | 
			
		||||
            doc_d = GridDocument(the_file=six.b(""))
 | 
			
		||||
            doc_d.save()
 | 
			
		||||
 | 
			
		||||
            doc_e = GridDocument.objects.with_id(doc_d.id)
 | 
			
		||||
            self.assertEqual(doc_d.the_file.grid_id, doc_e.the_file.grid_id)
 | 
			
		||||
 | 
			
		||||
            doc_e.the_file.replace(f, filename='doc_e')
 | 
			
		||||
            doc_e.the_file.replace(f, filename="doc_e")
 | 
			
		||||
            doc_e.save()
 | 
			
		||||
 | 
			
		||||
            doc_f = GridDocument.objects.with_id(doc_e.id)
 | 
			
		||||
@@ -222,11 +226,12 @@ class FileTest(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        db = GridDocument._get_db()
 | 
			
		||||
        grid_fs = gridfs.GridFS(db)
 | 
			
		||||
        self.assertEqual(['doc_b', 'doc_e'], grid_fs.list())
 | 
			
		||||
        self.assertEqual(["doc_b", "doc_e"], grid_fs.list())
 | 
			
		||||
 | 
			
		||||
    def test_file_uniqueness(self):
 | 
			
		||||
        """Ensure that each instance of a FileField is unique
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class TestFile(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            the_file = FileField()
 | 
			
		||||
@@ -234,7 +239,7 @@ class FileTest(MongoDBTestCase):
 | 
			
		||||
        # First instance
 | 
			
		||||
        test_file = TestFile()
 | 
			
		||||
        test_file.name = "Hello, World!"
 | 
			
		||||
        test_file.the_file.put(six.b('Hello, World!'))
 | 
			
		||||
        test_file.the_file.put(six.b("Hello, World!"))
 | 
			
		||||
        test_file.save()
 | 
			
		||||
 | 
			
		||||
        # Second instance
 | 
			
		||||
@@ -255,20 +260,21 @@ class FileTest(MongoDBTestCase):
 | 
			
		||||
            photo = FileField()
 | 
			
		||||
 | 
			
		||||
        Animal.drop_collection()
 | 
			
		||||
        marmot = Animal(genus='Marmota', family='Sciuridae')
 | 
			
		||||
        marmot = Animal(genus="Marmota", family="Sciuridae")
 | 
			
		||||
 | 
			
		||||
        marmot_photo_content = get_file(TEST_IMAGE_PATH)  # Retrieve a photo from disk
 | 
			
		||||
        marmot.photo.put(marmot_photo_content, content_type='image/jpeg', foo='bar')
 | 
			
		||||
        marmot.photo.put(marmot_photo_content, content_type="image/jpeg", foo="bar")
 | 
			
		||||
        marmot.photo.close()
 | 
			
		||||
        marmot.save()
 | 
			
		||||
 | 
			
		||||
        marmot = Animal.objects.get()
 | 
			
		||||
        self.assertEqual(marmot.photo.content_type, 'image/jpeg')
 | 
			
		||||
        self.assertEqual(marmot.photo.foo, 'bar')
 | 
			
		||||
        self.assertEqual(marmot.photo.content_type, "image/jpeg")
 | 
			
		||||
        self.assertEqual(marmot.photo.foo, "bar")
 | 
			
		||||
 | 
			
		||||
    def test_file_reassigning(self):
 | 
			
		||||
        class TestFile(Document):
 | 
			
		||||
            the_file = FileField()
 | 
			
		||||
 | 
			
		||||
        TestFile.drop_collection()
 | 
			
		||||
 | 
			
		||||
        test_file = TestFile(the_file=get_file(TEST_IMAGE_PATH)).save()
 | 
			
		||||
@@ -282,13 +288,15 @@ class FileTest(MongoDBTestCase):
 | 
			
		||||
    def test_file_boolean(self):
 | 
			
		||||
        """Ensure that a boolean test of a FileField indicates its presence
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class TestFile(Document):
 | 
			
		||||
            the_file = FileField()
 | 
			
		||||
 | 
			
		||||
        TestFile.drop_collection()
 | 
			
		||||
 | 
			
		||||
        test_file = TestFile()
 | 
			
		||||
        self.assertFalse(bool(test_file.the_file))
 | 
			
		||||
        test_file.the_file.put(six.b('Hello, World!'), content_type='text/plain')
 | 
			
		||||
        test_file.the_file.put(six.b("Hello, World!"), content_type="text/plain")
 | 
			
		||||
        test_file.save()
 | 
			
		||||
        self.assertTrue(bool(test_file.the_file))
 | 
			
		||||
 | 
			
		||||
@@ -297,6 +305,7 @@ class FileTest(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_file_cmp(self):
 | 
			
		||||
        """Test comparing against other types"""
 | 
			
		||||
 | 
			
		||||
        class TestFile(Document):
 | 
			
		||||
            the_file = FileField()
 | 
			
		||||
 | 
			
		||||
@@ -305,11 +314,12 @@ class FileTest(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_file_disk_space(self):
 | 
			
		||||
        """ Test disk space usage when we delete/replace a file """
 | 
			
		||||
 | 
			
		||||
        class TestFile(Document):
 | 
			
		||||
            the_file = FileField()
 | 
			
		||||
 | 
			
		||||
        text = six.b('Hello, World!')
 | 
			
		||||
        content_type = 'text/plain'
 | 
			
		||||
        text = six.b("Hello, World!")
 | 
			
		||||
        content_type = "text/plain"
 | 
			
		||||
 | 
			
		||||
        testfile = TestFile()
 | 
			
		||||
        testfile.the_file.put(text, content_type=content_type, filename="hello")
 | 
			
		||||
@@ -352,7 +362,7 @@ class FileTest(MongoDBTestCase):
 | 
			
		||||
        testfile.the_file.put(text, content_type=content_type, filename="hello")
 | 
			
		||||
        testfile.save()
 | 
			
		||||
 | 
			
		||||
        text = six.b('Bonjour, World!')
 | 
			
		||||
        text = six.b("Bonjour, World!")
 | 
			
		||||
        testfile.the_file.replace(text, content_type=content_type, filename="hello")
 | 
			
		||||
        testfile.save()
 | 
			
		||||
 | 
			
		||||
@@ -370,7 +380,7 @@ class FileTest(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_image_field(self):
 | 
			
		||||
        if not HAS_PIL:
 | 
			
		||||
            raise SkipTest('PIL not installed')
 | 
			
		||||
            raise SkipTest("PIL not installed")
 | 
			
		||||
 | 
			
		||||
        class TestImage(Document):
 | 
			
		||||
            image = ImageField()
 | 
			
		||||
@@ -386,7 +396,9 @@ class FileTest(MongoDBTestCase):
 | 
			
		||||
                t.image.put(f)
 | 
			
		||||
                self.fail("Should have raised an invalidation error")
 | 
			
		||||
            except ValidationError as e:
 | 
			
		||||
                self.assertEqual("%s" % e, "Invalid image: cannot identify image file %s" % f)
 | 
			
		||||
                self.assertEqual(
 | 
			
		||||
                    "%s" % e, "Invalid image: cannot identify image file %s" % f
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
        t = TestImage()
 | 
			
		||||
        t.image.put(get_file(TEST_IMAGE_PATH))
 | 
			
		||||
@@ -394,7 +406,7 @@ class FileTest(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        t = TestImage.objects.first()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(t.image.format, 'PNG')
 | 
			
		||||
        self.assertEqual(t.image.format, "PNG")
 | 
			
		||||
 | 
			
		||||
        w, h = t.image.size
 | 
			
		||||
        self.assertEqual(w, 371)
 | 
			
		||||
@@ -404,10 +416,11 @@ class FileTest(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_image_field_reassigning(self):
 | 
			
		||||
        if not HAS_PIL:
 | 
			
		||||
            raise SkipTest('PIL not installed')
 | 
			
		||||
            raise SkipTest("PIL not installed")
 | 
			
		||||
 | 
			
		||||
        class TestFile(Document):
 | 
			
		||||
            the_file = ImageField()
 | 
			
		||||
 | 
			
		||||
        TestFile.drop_collection()
 | 
			
		||||
 | 
			
		||||
        test_file = TestFile(the_file=get_file(TEST_IMAGE_PATH)).save()
 | 
			
		||||
@@ -420,7 +433,7 @@ class FileTest(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_image_field_resize(self):
 | 
			
		||||
        if not HAS_PIL:
 | 
			
		||||
            raise SkipTest('PIL not installed')
 | 
			
		||||
            raise SkipTest("PIL not installed")
 | 
			
		||||
 | 
			
		||||
        class TestImage(Document):
 | 
			
		||||
            image = ImageField(size=(185, 37))
 | 
			
		||||
@@ -433,7 +446,7 @@ class FileTest(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        t = TestImage.objects.first()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(t.image.format, 'PNG')
 | 
			
		||||
        self.assertEqual(t.image.format, "PNG")
 | 
			
		||||
        w, h = t.image.size
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(w, 185)
 | 
			
		||||
@@ -443,7 +456,7 @@ class FileTest(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_image_field_resize_force(self):
 | 
			
		||||
        if not HAS_PIL:
 | 
			
		||||
            raise SkipTest('PIL not installed')
 | 
			
		||||
            raise SkipTest("PIL not installed")
 | 
			
		||||
 | 
			
		||||
        class TestImage(Document):
 | 
			
		||||
            image = ImageField(size=(185, 37, True))
 | 
			
		||||
@@ -456,7 +469,7 @@ class FileTest(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        t = TestImage.objects.first()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(t.image.format, 'PNG')
 | 
			
		||||
        self.assertEqual(t.image.format, "PNG")
 | 
			
		||||
        w, h = t.image.size
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(w, 185)
 | 
			
		||||
@@ -466,7 +479,7 @@ class FileTest(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_image_field_thumbnail(self):
 | 
			
		||||
        if not HAS_PIL:
 | 
			
		||||
            raise SkipTest('PIL not installed')
 | 
			
		||||
            raise SkipTest("PIL not installed")
 | 
			
		||||
 | 
			
		||||
        class TestImage(Document):
 | 
			
		||||
            image = ImageField(thumbnail_size=(92, 18))
 | 
			
		||||
@@ -479,19 +492,18 @@ class FileTest(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        t = TestImage.objects.first()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(t.image.thumbnail.format, 'PNG')
 | 
			
		||||
        self.assertEqual(t.image.thumbnail.format, "PNG")
 | 
			
		||||
        self.assertEqual(t.image.thumbnail.width, 92)
 | 
			
		||||
        self.assertEqual(t.image.thumbnail.height, 18)
 | 
			
		||||
 | 
			
		||||
        t.image.delete()
 | 
			
		||||
 | 
			
		||||
    def test_file_multidb(self):
 | 
			
		||||
        register_connection('test_files', 'test_files')
 | 
			
		||||
        register_connection("test_files", "test_files")
 | 
			
		||||
 | 
			
		||||
        class TestFile(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            the_file = FileField(db_alias="test_files",
 | 
			
		||||
                                 collection_name="macumba")
 | 
			
		||||
            the_file = FileField(db_alias="test_files", collection_name="macumba")
 | 
			
		||||
 | 
			
		||||
        TestFile.drop_collection()
 | 
			
		||||
 | 
			
		||||
@@ -502,23 +514,21 @@ class FileTest(MongoDBTestCase):
 | 
			
		||||
        # First instance
 | 
			
		||||
        test_file = TestFile()
 | 
			
		||||
        test_file.name = "Hello, World!"
 | 
			
		||||
        test_file.the_file.put(six.b('Hello, World!'),
 | 
			
		||||
                          name="hello.txt")
 | 
			
		||||
        test_file.the_file.put(six.b("Hello, World!"), name="hello.txt")
 | 
			
		||||
        test_file.save()
 | 
			
		||||
 | 
			
		||||
        data = get_db("test_files").macumba.files.find_one()
 | 
			
		||||
        self.assertEqual(data.get('name'), 'hello.txt')
 | 
			
		||||
        self.assertEqual(data.get("name"), "hello.txt")
 | 
			
		||||
 | 
			
		||||
        test_file = TestFile.objects.first()
 | 
			
		||||
        self.assertEqual(test_file.the_file.read(), six.b('Hello, World!'))
 | 
			
		||||
        self.assertEqual(test_file.the_file.read(), six.b("Hello, World!"))
 | 
			
		||||
 | 
			
		||||
        test_file = TestFile.objects.first()
 | 
			
		||||
        test_file.the_file = six.b('HELLO, WORLD!')
 | 
			
		||||
        test_file.the_file = six.b("HELLO, WORLD!")
 | 
			
		||||
        test_file.save()
 | 
			
		||||
 | 
			
		||||
        test_file = TestFile.objects.first()
 | 
			
		||||
        self.assertEqual(test_file.the_file.read(),
 | 
			
		||||
                         six.b('HELLO, WORLD!'))
 | 
			
		||||
        self.assertEqual(test_file.the_file.read(), six.b("HELLO, WORLD!"))
 | 
			
		||||
 | 
			
		||||
    def test_copyable(self):
 | 
			
		||||
        class PutFile(Document):
 | 
			
		||||
@@ -526,8 +536,8 @@ class FileTest(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        PutFile.drop_collection()
 | 
			
		||||
 | 
			
		||||
        text = six.b('Hello, World!')
 | 
			
		||||
        content_type = 'text/plain'
 | 
			
		||||
        text = six.b("Hello, World!")
 | 
			
		||||
        content_type = "text/plain"
 | 
			
		||||
 | 
			
		||||
        putfile = PutFile()
 | 
			
		||||
        putfile.the_file.put(text, content_type=content_type)
 | 
			
		||||
@@ -542,7 +552,7 @@ class FileTest(MongoDBTestCase):
 | 
			
		||||
    def test_get_image_by_grid_id(self):
 | 
			
		||||
 | 
			
		||||
        if not HAS_PIL:
 | 
			
		||||
            raise SkipTest('PIL not installed')
 | 
			
		||||
            raise SkipTest("PIL not installed")
 | 
			
		||||
 | 
			
		||||
        class TestImage(Document):
 | 
			
		||||
 | 
			
		||||
@@ -559,8 +569,9 @@ class FileTest(MongoDBTestCase):
 | 
			
		||||
        test = TestImage.objects.first()
 | 
			
		||||
        grid_id = test.image1.grid_id
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(1, TestImage.objects(Q(image1=grid_id)
 | 
			
		||||
                                              or Q(image2=grid_id)).count())
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            1, TestImage.objects(Q(image1=grid_id) or Q(image2=grid_id)).count()
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_complex_field_filefield(self):
 | 
			
		||||
        """Ensure you can add meta data to file"""
 | 
			
		||||
@@ -571,21 +582,21 @@ class FileTest(MongoDBTestCase):
 | 
			
		||||
            photos = ListField(FileField())
 | 
			
		||||
 | 
			
		||||
        Animal.drop_collection()
 | 
			
		||||
        marmot = Animal(genus='Marmota', family='Sciuridae')
 | 
			
		||||
        marmot = Animal(genus="Marmota", family="Sciuridae")
 | 
			
		||||
 | 
			
		||||
        with open(TEST_IMAGE_PATH, 'rb') as marmot_photo:   # Retrieve a photo from disk
 | 
			
		||||
            photos_field = marmot._fields['photos'].field
 | 
			
		||||
            new_proxy = photos_field.get_proxy_obj('photos', marmot)
 | 
			
		||||
            new_proxy.put(marmot_photo, content_type='image/jpeg', foo='bar')
 | 
			
		||||
        with open(TEST_IMAGE_PATH, "rb") as marmot_photo:  # Retrieve a photo from disk
 | 
			
		||||
            photos_field = marmot._fields["photos"].field
 | 
			
		||||
            new_proxy = photos_field.get_proxy_obj("photos", marmot)
 | 
			
		||||
            new_proxy.put(marmot_photo, content_type="image/jpeg", foo="bar")
 | 
			
		||||
 | 
			
		||||
        marmot.photos.append(new_proxy)
 | 
			
		||||
        marmot.save()
 | 
			
		||||
 | 
			
		||||
        marmot = Animal.objects.get()
 | 
			
		||||
        self.assertEqual(marmot.photos[0].content_type, 'image/jpeg')
 | 
			
		||||
        self.assertEqual(marmot.photos[0].foo, 'bar')
 | 
			
		||||
        self.assertEqual(marmot.photos[0].content_type, "image/jpeg")
 | 
			
		||||
        self.assertEqual(marmot.photos[0].foo, "bar")
 | 
			
		||||
        self.assertEqual(marmot.photos[0].get().length, 8313)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    unittest.main()
 | 
			
		||||
 
 | 
			
		||||
@@ -4,28 +4,27 @@ import unittest
 | 
			
		||||
from mongoengine import *
 | 
			
		||||
from mongoengine.connection import get_db
 | 
			
		||||
 | 
			
		||||
__all__ = ("GeoFieldTest", )
 | 
			
		||||
__all__ = ("GeoFieldTest",)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GeoFieldTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        connect(db='mongoenginetest')
 | 
			
		||||
        connect(db="mongoenginetest")
 | 
			
		||||
        self.db = get_db()
 | 
			
		||||
 | 
			
		||||
    def _test_for_expected_error(self, Cls, loc, expected):
 | 
			
		||||
        try:
 | 
			
		||||
            Cls(loc=loc).validate()
 | 
			
		||||
            self.fail('Should not validate the location {0}'.format(loc))
 | 
			
		||||
            self.fail("Should not validate the location {0}".format(loc))
 | 
			
		||||
        except ValidationError as e:
 | 
			
		||||
            self.assertEqual(expected, e.to_dict()['loc'])
 | 
			
		||||
            self.assertEqual(expected, e.to_dict()["loc"])
 | 
			
		||||
 | 
			
		||||
    def test_geopoint_validation(self):
 | 
			
		||||
        class Location(Document):
 | 
			
		||||
            loc = GeoPointField()
 | 
			
		||||
 | 
			
		||||
        invalid_coords = [{"x": 1, "y": 2}, 5, "a"]
 | 
			
		||||
        expected = 'GeoPointField can only accept tuples or lists of (x, y)'
 | 
			
		||||
        expected = "GeoPointField can only accept tuples or lists of (x, y)"
 | 
			
		||||
 | 
			
		||||
        for coord in invalid_coords:
 | 
			
		||||
            self._test_for_expected_error(Location, coord, expected)
 | 
			
		||||
@@ -40,7 +39,7 @@ class GeoFieldTest(unittest.TestCase):
 | 
			
		||||
            expected = "Both values (%s) in point must be float or int" % repr(coord)
 | 
			
		||||
            self._test_for_expected_error(Location, coord, expected)
 | 
			
		||||
 | 
			
		||||
        invalid_coords = [21, 4, 'a']
 | 
			
		||||
        invalid_coords = [21, 4, "a"]
 | 
			
		||||
        for coord in invalid_coords:
 | 
			
		||||
            expected = "GeoPointField can only accept tuples or lists of (x, y)"
 | 
			
		||||
            self._test_for_expected_error(Location, coord, expected)
 | 
			
		||||
@@ -50,7 +49,9 @@ class GeoFieldTest(unittest.TestCase):
 | 
			
		||||
            loc = PointField()
 | 
			
		||||
 | 
			
		||||
        invalid_coords = {"x": 1, "y": 2}
 | 
			
		||||
        expected = 'PointField can only accept a valid GeoJson dictionary or lists of (x, y)'
 | 
			
		||||
        expected = (
 | 
			
		||||
            "PointField can only accept a valid GeoJson dictionary or lists of (x, y)"
 | 
			
		||||
        )
 | 
			
		||||
        self._test_for_expected_error(Location, invalid_coords, expected)
 | 
			
		||||
 | 
			
		||||
        invalid_coords = {"type": "MadeUp", "coordinates": []}
 | 
			
		||||
@@ -77,19 +78,16 @@ class GeoFieldTest(unittest.TestCase):
 | 
			
		||||
            self._test_for_expected_error(Location, coord, expected)
 | 
			
		||||
 | 
			
		||||
        Location(loc=[1, 2]).validate()
 | 
			
		||||
        Location(loc={
 | 
			
		||||
            "type": "Point",
 | 
			
		||||
            "coordinates": [
 | 
			
		||||
              81.4471435546875,
 | 
			
		||||
              23.61432859499169
 | 
			
		||||
            ]}).validate()
 | 
			
		||||
        Location(
 | 
			
		||||
            loc={"type": "Point", "coordinates": [81.4471435546875, 23.61432859499169]}
 | 
			
		||||
        ).validate()
 | 
			
		||||
 | 
			
		||||
    def test_linestring_validation(self):
 | 
			
		||||
        class Location(Document):
 | 
			
		||||
            loc = LineStringField()
 | 
			
		||||
 | 
			
		||||
        invalid_coords = {"x": 1, "y": 2}
 | 
			
		||||
        expected = 'LineStringField can only accept a valid GeoJson dictionary or lists of (x, y)'
 | 
			
		||||
        expected = "LineStringField can only accept a valid GeoJson dictionary or lists of (x, y)"
 | 
			
		||||
        self._test_for_expected_error(Location, invalid_coords, expected)
 | 
			
		||||
 | 
			
		||||
        invalid_coords = {"type": "MadeUp", "coordinates": [[]]}
 | 
			
		||||
@@ -97,7 +95,9 @@ class GeoFieldTest(unittest.TestCase):
 | 
			
		||||
        self._test_for_expected_error(Location, invalid_coords, expected)
 | 
			
		||||
 | 
			
		||||
        invalid_coords = {"type": "LineString", "coordinates": [[1, 2, 3]]}
 | 
			
		||||
        expected = "Invalid LineString:\nValue ([1, 2, 3]) must be a two-dimensional point"
 | 
			
		||||
        expected = (
 | 
			
		||||
            "Invalid LineString:\nValue ([1, 2, 3]) must be a two-dimensional point"
 | 
			
		||||
        )
 | 
			
		||||
        self._test_for_expected_error(Location, invalid_coords, expected)
 | 
			
		||||
 | 
			
		||||
        invalid_coords = [5, "a"]
 | 
			
		||||
@@ -105,16 +105,25 @@ class GeoFieldTest(unittest.TestCase):
 | 
			
		||||
        self._test_for_expected_error(Location, invalid_coords, expected)
 | 
			
		||||
 | 
			
		||||
        invalid_coords = [[1]]
 | 
			
		||||
        expected = "Invalid LineString:\nValue (%s) must be a two-dimensional point" % repr(invalid_coords[0])
 | 
			
		||||
        expected = (
 | 
			
		||||
            "Invalid LineString:\nValue (%s) must be a two-dimensional point"
 | 
			
		||||
            % repr(invalid_coords[0])
 | 
			
		||||
        )
 | 
			
		||||
        self._test_for_expected_error(Location, invalid_coords, expected)
 | 
			
		||||
 | 
			
		||||
        invalid_coords = [[1, 2, 3]]
 | 
			
		||||
        expected = "Invalid LineString:\nValue (%s) must be a two-dimensional point" % repr(invalid_coords[0])
 | 
			
		||||
        expected = (
 | 
			
		||||
            "Invalid LineString:\nValue (%s) must be a two-dimensional point"
 | 
			
		||||
            % repr(invalid_coords[0])
 | 
			
		||||
        )
 | 
			
		||||
        self._test_for_expected_error(Location, invalid_coords, expected)
 | 
			
		||||
 | 
			
		||||
        invalid_coords = [[[{}, {}]], [("a", "b")]]
 | 
			
		||||
        for coord in invalid_coords:
 | 
			
		||||
            expected = "Invalid LineString:\nBoth values (%s) in point must be float or int" % repr(coord[0])
 | 
			
		||||
            expected = (
 | 
			
		||||
                "Invalid LineString:\nBoth values (%s) in point must be float or int"
 | 
			
		||||
                % repr(coord[0])
 | 
			
		||||
            )
 | 
			
		||||
            self._test_for_expected_error(Location, coord, expected)
 | 
			
		||||
 | 
			
		||||
        Location(loc=[[1, 2], [3, 4], [5, 6], [1, 2]]).validate()
 | 
			
		||||
@@ -124,7 +133,9 @@ class GeoFieldTest(unittest.TestCase):
 | 
			
		||||
            loc = PolygonField()
 | 
			
		||||
 | 
			
		||||
        invalid_coords = {"x": 1, "y": 2}
 | 
			
		||||
        expected = 'PolygonField can only accept a valid GeoJson dictionary or lists of (x, y)'
 | 
			
		||||
        expected = (
 | 
			
		||||
            "PolygonField can only accept a valid GeoJson dictionary or lists of (x, y)"
 | 
			
		||||
        )
 | 
			
		||||
        self._test_for_expected_error(Location, invalid_coords, expected)
 | 
			
		||||
 | 
			
		||||
        invalid_coords = {"type": "MadeUp", "coordinates": [[]]}
 | 
			
		||||
@@ -136,7 +147,9 @@ class GeoFieldTest(unittest.TestCase):
 | 
			
		||||
        self._test_for_expected_error(Location, invalid_coords, expected)
 | 
			
		||||
 | 
			
		||||
        invalid_coords = [[[5, "a"]]]
 | 
			
		||||
        expected = "Invalid Polygon:\nBoth values ([5, 'a']) in point must be float or int"
 | 
			
		||||
        expected = (
 | 
			
		||||
            "Invalid Polygon:\nBoth values ([5, 'a']) in point must be float or int"
 | 
			
		||||
        )
 | 
			
		||||
        self._test_for_expected_error(Location, invalid_coords, expected)
 | 
			
		||||
 | 
			
		||||
        invalid_coords = [[[]]]
 | 
			
		||||
@@ -162,7 +175,7 @@ class GeoFieldTest(unittest.TestCase):
 | 
			
		||||
            loc = MultiPointField()
 | 
			
		||||
 | 
			
		||||
        invalid_coords = {"x": 1, "y": 2}
 | 
			
		||||
        expected = 'MultiPointField can only accept a valid GeoJson dictionary or lists of (x, y)'
 | 
			
		||||
        expected = "MultiPointField can only accept a valid GeoJson dictionary or lists of (x, y)"
 | 
			
		||||
        self._test_for_expected_error(Location, invalid_coords, expected)
 | 
			
		||||
 | 
			
		||||
        invalid_coords = {"type": "MadeUp", "coordinates": [[]]}
 | 
			
		||||
@@ -188,19 +201,19 @@ class GeoFieldTest(unittest.TestCase):
 | 
			
		||||
            self._test_for_expected_error(Location, coord, expected)
 | 
			
		||||
 | 
			
		||||
        Location(loc=[[1, 2]]).validate()
 | 
			
		||||
        Location(loc={
 | 
			
		||||
            "type": "MultiPoint",
 | 
			
		||||
            "coordinates": [
 | 
			
		||||
                [1, 2],
 | 
			
		||||
                [81.4471435546875, 23.61432859499169]
 | 
			
		||||
            ]}).validate()
 | 
			
		||||
        Location(
 | 
			
		||||
            loc={
 | 
			
		||||
                "type": "MultiPoint",
 | 
			
		||||
                "coordinates": [[1, 2], [81.4471435546875, 23.61432859499169]],
 | 
			
		||||
            }
 | 
			
		||||
        ).validate()
 | 
			
		||||
 | 
			
		||||
    def test_multilinestring_validation(self):
 | 
			
		||||
        class Location(Document):
 | 
			
		||||
            loc = MultiLineStringField()
 | 
			
		||||
 | 
			
		||||
        invalid_coords = {"x": 1, "y": 2}
 | 
			
		||||
        expected = 'MultiLineStringField can only accept a valid GeoJson dictionary or lists of (x, y)'
 | 
			
		||||
        expected = "MultiLineStringField can only accept a valid GeoJson dictionary or lists of (x, y)"
 | 
			
		||||
        self._test_for_expected_error(Location, invalid_coords, expected)
 | 
			
		||||
 | 
			
		||||
        invalid_coords = {"type": "MadeUp", "coordinates": [[]]}
 | 
			
		||||
@@ -216,16 +229,25 @@ class GeoFieldTest(unittest.TestCase):
 | 
			
		||||
        self._test_for_expected_error(Location, invalid_coords, expected)
 | 
			
		||||
 | 
			
		||||
        invalid_coords = [[[1]]]
 | 
			
		||||
        expected = "Invalid MultiLineString:\nValue (%s) must be a two-dimensional point" % repr(invalid_coords[0][0])
 | 
			
		||||
        expected = (
 | 
			
		||||
            "Invalid MultiLineString:\nValue (%s) must be a two-dimensional point"
 | 
			
		||||
            % repr(invalid_coords[0][0])
 | 
			
		||||
        )
 | 
			
		||||
        self._test_for_expected_error(Location, invalid_coords, expected)
 | 
			
		||||
 | 
			
		||||
        invalid_coords = [[[1, 2, 3]]]
 | 
			
		||||
        expected = "Invalid MultiLineString:\nValue (%s) must be a two-dimensional point" % repr(invalid_coords[0][0])
 | 
			
		||||
        expected = (
 | 
			
		||||
            "Invalid MultiLineString:\nValue (%s) must be a two-dimensional point"
 | 
			
		||||
            % repr(invalid_coords[0][0])
 | 
			
		||||
        )
 | 
			
		||||
        self._test_for_expected_error(Location, invalid_coords, expected)
 | 
			
		||||
 | 
			
		||||
        invalid_coords = [[[[{}, {}]]], [[("a", "b")]]]
 | 
			
		||||
        for coord in invalid_coords:
 | 
			
		||||
            expected = "Invalid MultiLineString:\nBoth values (%s) in point must be float or int" % repr(coord[0][0])
 | 
			
		||||
            expected = (
 | 
			
		||||
                "Invalid MultiLineString:\nBoth values (%s) in point must be float or int"
 | 
			
		||||
                % repr(coord[0][0])
 | 
			
		||||
            )
 | 
			
		||||
            self._test_for_expected_error(Location, coord, expected)
 | 
			
		||||
 | 
			
		||||
        Location(loc=[[[1, 2], [3, 4], [5, 6], [1, 2]]]).validate()
 | 
			
		||||
@@ -235,7 +257,7 @@ class GeoFieldTest(unittest.TestCase):
 | 
			
		||||
            loc = MultiPolygonField()
 | 
			
		||||
 | 
			
		||||
        invalid_coords = {"x": 1, "y": 2}
 | 
			
		||||
        expected = 'MultiPolygonField can only accept a valid GeoJson dictionary or lists of (x, y)'
 | 
			
		||||
        expected = "MultiPolygonField can only accept a valid GeoJson dictionary or lists of (x, y)"
 | 
			
		||||
        self._test_for_expected_error(Location, invalid_coords, expected)
 | 
			
		||||
 | 
			
		||||
        invalid_coords = {"type": "MadeUp", "coordinates": [[]]}
 | 
			
		||||
@@ -243,7 +265,9 @@ class GeoFieldTest(unittest.TestCase):
 | 
			
		||||
        self._test_for_expected_error(Location, invalid_coords, expected)
 | 
			
		||||
 | 
			
		||||
        invalid_coords = {"type": "MultiPolygon", "coordinates": [[[[1, 2, 3]]]]}
 | 
			
		||||
        expected = "Invalid MultiPolygon:\nValue ([1, 2, 3]) must be a two-dimensional point"
 | 
			
		||||
        expected = (
 | 
			
		||||
            "Invalid MultiPolygon:\nValue ([1, 2, 3]) must be a two-dimensional point"
 | 
			
		||||
        )
 | 
			
		||||
        self._test_for_expected_error(Location, invalid_coords, expected)
 | 
			
		||||
 | 
			
		||||
        invalid_coords = [[[[5, "a"]]]]
 | 
			
		||||
@@ -255,7 +279,9 @@ class GeoFieldTest(unittest.TestCase):
 | 
			
		||||
        self._test_for_expected_error(Location, invalid_coords, expected)
 | 
			
		||||
 | 
			
		||||
        invalid_coords = [[[[1, 2, 3]]]]
 | 
			
		||||
        expected = "Invalid MultiPolygon:\nValue ([1, 2, 3]) must be a two-dimensional point"
 | 
			
		||||
        expected = (
 | 
			
		||||
            "Invalid MultiPolygon:\nValue ([1, 2, 3]) must be a two-dimensional point"
 | 
			
		||||
        )
 | 
			
		||||
        self._test_for_expected_error(Location, invalid_coords, expected)
 | 
			
		||||
 | 
			
		||||
        invalid_coords = [[[[{}, {}]]], [[("a", "b")]]]
 | 
			
		||||
@@ -263,7 +289,9 @@ class GeoFieldTest(unittest.TestCase):
 | 
			
		||||
        self._test_for_expected_error(Location, invalid_coords, expected)
 | 
			
		||||
 | 
			
		||||
        invalid_coords = [[[[1, 2], [3, 4]]]]
 | 
			
		||||
        expected = "Invalid MultiPolygon:\nLineStrings must start and end at the same point"
 | 
			
		||||
        expected = (
 | 
			
		||||
            "Invalid MultiPolygon:\nLineStrings must start and end at the same point"
 | 
			
		||||
        )
 | 
			
		||||
        self._test_for_expected_error(Location, invalid_coords, expected)
 | 
			
		||||
 | 
			
		||||
        Location(loc=[[[[1, 2], [3, 4], [5, 6], [1, 2]]]]).validate()
 | 
			
		||||
@@ -271,17 +299,19 @@ class GeoFieldTest(unittest.TestCase):
 | 
			
		||||
    def test_indexes_geopoint(self):
 | 
			
		||||
        """Ensure that indexes are created automatically for GeoPointFields.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Event(Document):
 | 
			
		||||
            title = StringField()
 | 
			
		||||
            location = GeoPointField()
 | 
			
		||||
 | 
			
		||||
        geo_indicies = Event._geo_indices()
 | 
			
		||||
        self.assertEqual(geo_indicies, [{'fields': [('location', '2d')]}])
 | 
			
		||||
        self.assertEqual(geo_indicies, [{"fields": [("location", "2d")]}])
 | 
			
		||||
 | 
			
		||||
    def test_geopoint_embedded_indexes(self):
 | 
			
		||||
        """Ensure that indexes are created automatically for GeoPointFields on
 | 
			
		||||
        embedded documents.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Venue(EmbeddedDocument):
 | 
			
		||||
            location = GeoPointField()
 | 
			
		||||
            name = StringField()
 | 
			
		||||
@@ -291,11 +321,12 @@ class GeoFieldTest(unittest.TestCase):
 | 
			
		||||
            venue = EmbeddedDocumentField(Venue)
 | 
			
		||||
 | 
			
		||||
        geo_indicies = Event._geo_indices()
 | 
			
		||||
        self.assertEqual(geo_indicies, [{'fields': [('venue.location', '2d')]}])
 | 
			
		||||
        self.assertEqual(geo_indicies, [{"fields": [("venue.location", "2d")]}])
 | 
			
		||||
 | 
			
		||||
    def test_indexes_2dsphere(self):
 | 
			
		||||
        """Ensure that indexes are created automatically for GeoPointFields.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Event(Document):
 | 
			
		||||
            title = StringField()
 | 
			
		||||
            point = PointField()
 | 
			
		||||
@@ -303,13 +334,14 @@ class GeoFieldTest(unittest.TestCase):
 | 
			
		||||
            polygon = PolygonField()
 | 
			
		||||
 | 
			
		||||
        geo_indicies = Event._geo_indices()
 | 
			
		||||
        self.assertIn({'fields': [('line', '2dsphere')]}, geo_indicies)
 | 
			
		||||
        self.assertIn({'fields': [('polygon', '2dsphere')]}, geo_indicies)
 | 
			
		||||
        self.assertIn({'fields': [('point', '2dsphere')]}, geo_indicies)
 | 
			
		||||
        self.assertIn({"fields": [("line", "2dsphere")]}, geo_indicies)
 | 
			
		||||
        self.assertIn({"fields": [("polygon", "2dsphere")]}, geo_indicies)
 | 
			
		||||
        self.assertIn({"fields": [("point", "2dsphere")]}, geo_indicies)
 | 
			
		||||
 | 
			
		||||
    def test_indexes_2dsphere_embedded(self):
 | 
			
		||||
        """Ensure that indexes are created automatically for GeoPointFields.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Venue(EmbeddedDocument):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            point = PointField()
 | 
			
		||||
@@ -321,12 +353,11 @@ class GeoFieldTest(unittest.TestCase):
 | 
			
		||||
            venue = EmbeddedDocumentField(Venue)
 | 
			
		||||
 | 
			
		||||
        geo_indicies = Event._geo_indices()
 | 
			
		||||
        self.assertIn({'fields': [('venue.line', '2dsphere')]}, geo_indicies)
 | 
			
		||||
        self.assertIn({'fields': [('venue.polygon', '2dsphere')]}, geo_indicies)
 | 
			
		||||
        self.assertIn({'fields': [('venue.point', '2dsphere')]}, geo_indicies)
 | 
			
		||||
        self.assertIn({"fields": [("venue.line", "2dsphere")]}, geo_indicies)
 | 
			
		||||
        self.assertIn({"fields": [("venue.polygon", "2dsphere")]}, geo_indicies)
 | 
			
		||||
        self.assertIn({"fields": [("venue.point", "2dsphere")]}, geo_indicies)
 | 
			
		||||
 | 
			
		||||
    def test_geo_indexes_recursion(self):
 | 
			
		||||
 | 
			
		||||
        class Location(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            location = GeoPointField()
 | 
			
		||||
@@ -338,11 +369,11 @@ class GeoFieldTest(unittest.TestCase):
 | 
			
		||||
        Location.drop_collection()
 | 
			
		||||
        Parent.drop_collection()
 | 
			
		||||
 | 
			
		||||
        Parent(name='Berlin').save()
 | 
			
		||||
        Parent(name="Berlin").save()
 | 
			
		||||
        info = Parent._get_collection().index_information()
 | 
			
		||||
        self.assertNotIn('location_2d', info)
 | 
			
		||||
        self.assertNotIn("location_2d", info)
 | 
			
		||||
        info = Location._get_collection().index_information()
 | 
			
		||||
        self.assertIn('location_2d', info)
 | 
			
		||||
        self.assertIn("location_2d", info)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(len(Parent._geo_indices()), 0)
 | 
			
		||||
        self.assertEqual(len(Location._geo_indices()), 1)
 | 
			
		||||
@@ -354,9 +385,7 @@ class GeoFieldTest(unittest.TestCase):
 | 
			
		||||
            location = PointField(auto_index=False)
 | 
			
		||||
            datetime = DateTimeField()
 | 
			
		||||
 | 
			
		||||
            meta = {
 | 
			
		||||
                'indexes': [[("location", "2dsphere"), ("datetime", 1)]]
 | 
			
		||||
            }
 | 
			
		||||
            meta = {"indexes": [[("location", "2dsphere"), ("datetime", 1)]]}
 | 
			
		||||
 | 
			
		||||
        self.assertEqual([], Log._geo_indices())
 | 
			
		||||
 | 
			
		||||
@@ -364,8 +393,10 @@ class GeoFieldTest(unittest.TestCase):
 | 
			
		||||
        Log.ensure_indexes()
 | 
			
		||||
 | 
			
		||||
        info = Log._get_collection().index_information()
 | 
			
		||||
        self.assertEqual(info["location_2dsphere_datetime_1"]["key"],
 | 
			
		||||
                         [('location', '2dsphere'), ('datetime', 1)])
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            info["location_2dsphere_datetime_1"]["key"],
 | 
			
		||||
            [("location", "2dsphere"), ("datetime", 1)],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # Test listing explicitly
 | 
			
		||||
        class Log(Document):
 | 
			
		||||
@@ -373,9 +404,7 @@ class GeoFieldTest(unittest.TestCase):
 | 
			
		||||
            datetime = DateTimeField()
 | 
			
		||||
 | 
			
		||||
            meta = {
 | 
			
		||||
                'indexes': [
 | 
			
		||||
                    {'fields': [("location", "2dsphere"), ("datetime", 1)]}
 | 
			
		||||
                ]
 | 
			
		||||
                "indexes": [{"fields": [("location", "2dsphere"), ("datetime", 1)]}]
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        self.assertEqual([], Log._geo_indices())
 | 
			
		||||
@@ -384,9 +413,11 @@ class GeoFieldTest(unittest.TestCase):
 | 
			
		||||
        Log.ensure_indexes()
 | 
			
		||||
 | 
			
		||||
        info = Log._get_collection().index_information()
 | 
			
		||||
        self.assertEqual(info["location_2dsphere_datetime_1"]["key"],
 | 
			
		||||
                         [('location', '2dsphere'), ('datetime', 1)])
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            info["location_2dsphere_datetime_1"]["key"],
 | 
			
		||||
            [("location", "2dsphere"), ("datetime", 1)],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    unittest.main()
 | 
			
		||||
 
 | 
			
		||||
@@ -9,19 +9,22 @@ from bson import Binary
 | 
			
		||||
from mongoengine import *
 | 
			
		||||
from tests.utils import MongoDBTestCase
 | 
			
		||||
 | 
			
		||||
BIN_VALUE = six.b('\xa9\xf3\x8d(\xd7\x03\x84\xb4k[\x0f\xe3\xa2\x19\x85p[J\xa3\xd2>\xde\xe6\x87\xb1\x7f\xc6\xe6\xd9r\x18\xf5')
 | 
			
		||||
BIN_VALUE = six.b(
 | 
			
		||||
    "\xa9\xf3\x8d(\xd7\x03\x84\xb4k[\x0f\xe3\xa2\x19\x85p[J\xa3\xd2>\xde\xe6\x87\xb1\x7f\xc6\xe6\xd9r\x18\xf5"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestBinaryField(MongoDBTestCase):
 | 
			
		||||
    def test_binary_fields(self):
 | 
			
		||||
        """Ensure that binary fields can be stored and retrieved.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Attachment(Document):
 | 
			
		||||
            content_type = StringField()
 | 
			
		||||
            blob = BinaryField()
 | 
			
		||||
 | 
			
		||||
        BLOB = six.b('\xe6\x00\xc4\xff\x07')
 | 
			
		||||
        MIME_TYPE = 'application/octet-stream'
 | 
			
		||||
        BLOB = six.b("\xe6\x00\xc4\xff\x07")
 | 
			
		||||
        MIME_TYPE = "application/octet-stream"
 | 
			
		||||
 | 
			
		||||
        Attachment.drop_collection()
 | 
			
		||||
 | 
			
		||||
@@ -35,6 +38,7 @@ class TestBinaryField(MongoDBTestCase):
 | 
			
		||||
    def test_validation_succeeds(self):
 | 
			
		||||
        """Ensure that valid values can be assigned to binary fields.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class AttachmentRequired(Document):
 | 
			
		||||
            blob = BinaryField(required=True)
 | 
			
		||||
 | 
			
		||||
@@ -43,11 +47,11 @@ class TestBinaryField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        attachment_required = AttachmentRequired()
 | 
			
		||||
        self.assertRaises(ValidationError, attachment_required.validate)
 | 
			
		||||
        attachment_required.blob = Binary(six.b('\xe6\x00\xc4\xff\x07'))
 | 
			
		||||
        attachment_required.blob = Binary(six.b("\xe6\x00\xc4\xff\x07"))
 | 
			
		||||
        attachment_required.validate()
 | 
			
		||||
 | 
			
		||||
        _5_BYTES = six.b('\xe6\x00\xc4\xff\x07')
 | 
			
		||||
        _4_BYTES = six.b('\xe6\x00\xc4\xff')
 | 
			
		||||
        _5_BYTES = six.b("\xe6\x00\xc4\xff\x07")
 | 
			
		||||
        _4_BYTES = six.b("\xe6\x00\xc4\xff")
 | 
			
		||||
        self.assertRaises(ValidationError, AttachmentSizeLimit(blob=_5_BYTES).validate)
 | 
			
		||||
        AttachmentSizeLimit(blob=_4_BYTES).validate()
 | 
			
		||||
 | 
			
		||||
@@ -57,7 +61,7 @@ class TestBinaryField(MongoDBTestCase):
 | 
			
		||||
        class Attachment(Document):
 | 
			
		||||
            blob = BinaryField()
 | 
			
		||||
 | 
			
		||||
        for invalid_data in (2, u'Im_a_unicode', ['some_str']):
 | 
			
		||||
        for invalid_data in (2, u"Im_a_unicode", ["some_str"]):
 | 
			
		||||
            self.assertRaises(ValidationError, Attachment(blob=invalid_data).validate)
 | 
			
		||||
 | 
			
		||||
    def test__primary(self):
 | 
			
		||||
@@ -108,17 +112,17 @@ class TestBinaryField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_modify_operation__set(self):
 | 
			
		||||
        """Ensures no regression of bug #1127"""
 | 
			
		||||
 | 
			
		||||
        class MyDocument(Document):
 | 
			
		||||
            some_field = StringField()
 | 
			
		||||
            bin_field = BinaryField()
 | 
			
		||||
 | 
			
		||||
        MyDocument.drop_collection()
 | 
			
		||||
 | 
			
		||||
        doc = MyDocument.objects(some_field='test').modify(
 | 
			
		||||
            upsert=True, new=True,
 | 
			
		||||
            set__bin_field=BIN_VALUE
 | 
			
		||||
        doc = MyDocument.objects(some_field="test").modify(
 | 
			
		||||
            upsert=True, new=True, set__bin_field=BIN_VALUE
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(doc.some_field, 'test')
 | 
			
		||||
        self.assertEqual(doc.some_field, "test")
 | 
			
		||||
        if six.PY3:
 | 
			
		||||
            self.assertEqual(doc.bin_field, BIN_VALUE)
 | 
			
		||||
        else:
 | 
			
		||||
@@ -126,15 +130,18 @@ class TestBinaryField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_update_one(self):
 | 
			
		||||
        """Ensures no regression of bug #1127"""
 | 
			
		||||
 | 
			
		||||
        class MyDocument(Document):
 | 
			
		||||
            bin_field = BinaryField()
 | 
			
		||||
 | 
			
		||||
        MyDocument.drop_collection()
 | 
			
		||||
 | 
			
		||||
        bin_data = six.b('\xe6\x00\xc4\xff\x07')
 | 
			
		||||
        bin_data = six.b("\xe6\x00\xc4\xff\x07")
 | 
			
		||||
        doc = MyDocument(bin_field=bin_data).save()
 | 
			
		||||
 | 
			
		||||
        n_updated = MyDocument.objects(bin_field=bin_data).update_one(bin_field=BIN_VALUE)
 | 
			
		||||
        n_updated = MyDocument.objects(bin_field=bin_data).update_one(
 | 
			
		||||
            bin_field=BIN_VALUE
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(n_updated, 1)
 | 
			
		||||
        fetched = MyDocument.objects.with_id(doc.id)
 | 
			
		||||
        if six.PY3:
 | 
			
		||||
 
 | 
			
		||||
@@ -11,15 +11,13 @@ class TestBooleanField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        person = Person(admin=True)
 | 
			
		||||
        person.save()
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            get_as_pymongo(person),
 | 
			
		||||
            {'_id': person.id,
 | 
			
		||||
             'admin': True})
 | 
			
		||||
        self.assertEqual(get_as_pymongo(person), {"_id": person.id, "admin": True})
 | 
			
		||||
 | 
			
		||||
    def test_validation(self):
 | 
			
		||||
        """Ensure that invalid values cannot be assigned to boolean
 | 
			
		||||
        fields.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            admin = BooleanField()
 | 
			
		||||
 | 
			
		||||
@@ -29,9 +27,9 @@ class TestBooleanField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        person.admin = 2
 | 
			
		||||
        self.assertRaises(ValidationError, person.validate)
 | 
			
		||||
        person.admin = 'Yes'
 | 
			
		||||
        person.admin = "Yes"
 | 
			
		||||
        self.assertRaises(ValidationError, person.validate)
 | 
			
		||||
        person.admin = 'False'
 | 
			
		||||
        person.admin = "False"
 | 
			
		||||
        self.assertRaises(ValidationError, person.validate)
 | 
			
		||||
 | 
			
		||||
    def test_weirdness_constructor(self):
 | 
			
		||||
@@ -39,11 +37,12 @@ class TestBooleanField(MongoDBTestCase):
 | 
			
		||||
        which causes some weird behavior. We dont necessarily want to maintain this behavior
 | 
			
		||||
        but its a known issue
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            admin = BooleanField()
 | 
			
		||||
 | 
			
		||||
        new_person = Person(admin='False')
 | 
			
		||||
        new_person = Person(admin="False")
 | 
			
		||||
        self.assertTrue(new_person.admin)
 | 
			
		||||
 | 
			
		||||
        new_person = Person(admin='0')
 | 
			
		||||
        new_person = Person(admin="0")
 | 
			
		||||
        self.assertTrue(new_person.admin)
 | 
			
		||||
 
 | 
			
		||||
@@ -7,12 +7,12 @@ from tests.utils import MongoDBTestCase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestCachedReferenceField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_get_and_save(self):
 | 
			
		||||
        """
 | 
			
		||||
        Tests #1047: CachedReferenceField creates DBRefs on to_python,
 | 
			
		||||
        but can't save them on to_mongo.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Animal(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            tag = StringField()
 | 
			
		||||
@@ -24,10 +24,11 @@ class TestCachedReferenceField(MongoDBTestCase):
 | 
			
		||||
        Animal.drop_collection()
 | 
			
		||||
        Ocorrence.drop_collection()
 | 
			
		||||
 | 
			
		||||
        Ocorrence(person="testte",
 | 
			
		||||
                  animal=Animal(name="Leopard", tag="heavy").save()).save()
 | 
			
		||||
        Ocorrence(
 | 
			
		||||
            person="testte", animal=Animal(name="Leopard", tag="heavy").save()
 | 
			
		||||
        ).save()
 | 
			
		||||
        p = Ocorrence.objects.get()
 | 
			
		||||
        p.person = 'new_testte'
 | 
			
		||||
        p.person = "new_testte"
 | 
			
		||||
        p.save()
 | 
			
		||||
 | 
			
		||||
    def test_general_things(self):
 | 
			
		||||
@@ -37,8 +38,7 @@ class TestCachedReferenceField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        class Ocorrence(Document):
 | 
			
		||||
            person = StringField()
 | 
			
		||||
            animal = CachedReferenceField(
 | 
			
		||||
                Animal, fields=['tag'])
 | 
			
		||||
            animal = CachedReferenceField(Animal, fields=["tag"])
 | 
			
		||||
 | 
			
		||||
        Animal.drop_collection()
 | 
			
		||||
        Ocorrence.drop_collection()
 | 
			
		||||
@@ -55,19 +55,18 @@ class TestCachedReferenceField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(Ocorrence.objects(animal=None).count(), 1)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            a.to_mongo(fields=['tag']), {'tag': 'heavy', "_id": a.pk})
 | 
			
		||||
        self.assertEqual(a.to_mongo(fields=["tag"]), {"tag": "heavy", "_id": a.pk})
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(o.to_mongo()['animal']['tag'], 'heavy')
 | 
			
		||||
        self.assertEqual(o.to_mongo()["animal"]["tag"], "heavy")
 | 
			
		||||
 | 
			
		||||
        # counts
 | 
			
		||||
        Ocorrence(person="teste 2").save()
 | 
			
		||||
        Ocorrence(person="teste 3").save()
 | 
			
		||||
 | 
			
		||||
        count = Ocorrence.objects(animal__tag='heavy').count()
 | 
			
		||||
        count = Ocorrence.objects(animal__tag="heavy").count()
 | 
			
		||||
        self.assertEqual(count, 1)
 | 
			
		||||
 | 
			
		||||
        ocorrence = Ocorrence.objects(animal__tag='heavy').first()
 | 
			
		||||
        ocorrence = Ocorrence.objects(animal__tag="heavy").first()
 | 
			
		||||
        self.assertEqual(ocorrence.person, "teste")
 | 
			
		||||
        self.assertIsInstance(ocorrence.animal, Animal)
 | 
			
		||||
 | 
			
		||||
@@ -78,28 +77,21 @@ class TestCachedReferenceField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        class SocialTest(Document):
 | 
			
		||||
            group = StringField()
 | 
			
		||||
            person = CachedReferenceField(
 | 
			
		||||
                PersonAuto,
 | 
			
		||||
                fields=('salary',))
 | 
			
		||||
            person = CachedReferenceField(PersonAuto, fields=("salary",))
 | 
			
		||||
 | 
			
		||||
        PersonAuto.drop_collection()
 | 
			
		||||
        SocialTest.drop_collection()
 | 
			
		||||
 | 
			
		||||
        p = PersonAuto(name="Alberto", salary=Decimal('7000.00'))
 | 
			
		||||
        p = PersonAuto(name="Alberto", salary=Decimal("7000.00"))
 | 
			
		||||
        p.save()
 | 
			
		||||
 | 
			
		||||
        s = SocialTest(group="dev", person=p)
 | 
			
		||||
        s.save()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            SocialTest.objects._collection.find_one({'person.salary': 7000.00}), {
 | 
			
		||||
                '_id': s.pk,
 | 
			
		||||
                'group': s.group,
 | 
			
		||||
                'person': {
 | 
			
		||||
                    '_id': p.pk,
 | 
			
		||||
                    'salary': 7000.00
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
            SocialTest.objects._collection.find_one({"person.salary": 7000.00}),
 | 
			
		||||
            {"_id": s.pk, "group": s.group, "person": {"_id": p.pk, "salary": 7000.00}},
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_cached_reference_field_reference(self):
 | 
			
		||||
        class Group(Document):
 | 
			
		||||
@@ -111,17 +103,14 @@ class TestCachedReferenceField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        class SocialData(Document):
 | 
			
		||||
            obs = StringField()
 | 
			
		||||
            tags = ListField(
 | 
			
		||||
                StringField())
 | 
			
		||||
            person = CachedReferenceField(
 | 
			
		||||
                Person,
 | 
			
		||||
                fields=('group',))
 | 
			
		||||
            tags = ListField(StringField())
 | 
			
		||||
            person = CachedReferenceField(Person, fields=("group",))
 | 
			
		||||
 | 
			
		||||
        Group.drop_collection()
 | 
			
		||||
        Person.drop_collection()
 | 
			
		||||
        SocialData.drop_collection()
 | 
			
		||||
 | 
			
		||||
        g1 = Group(name='dev')
 | 
			
		||||
        g1 = Group(name="dev")
 | 
			
		||||
        g1.save()
 | 
			
		||||
 | 
			
		||||
        g2 = Group(name="designers")
 | 
			
		||||
@@ -136,22 +125,21 @@ class TestCachedReferenceField(MongoDBTestCase):
 | 
			
		||||
        p3 = Person(name="Afro design", group=g2)
 | 
			
		||||
        p3.save()
 | 
			
		||||
 | 
			
		||||
        s1 = SocialData(obs="testing 123", person=p1, tags=['tag1', 'tag2'])
 | 
			
		||||
        s1 = SocialData(obs="testing 123", person=p1, tags=["tag1", "tag2"])
 | 
			
		||||
        s1.save()
 | 
			
		||||
 | 
			
		||||
        s2 = SocialData(obs="testing 321", person=p3, tags=['tag3', 'tag4'])
 | 
			
		||||
        s2 = SocialData(obs="testing 321", person=p3, tags=["tag3", "tag4"])
 | 
			
		||||
        s2.save()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(SocialData.objects._collection.find_one(
 | 
			
		||||
            {'tags': 'tag2'}), {
 | 
			
		||||
                '_id': s1.pk,
 | 
			
		||||
                'obs': 'testing 123',
 | 
			
		||||
                'tags': ['tag1', 'tag2'],
 | 
			
		||||
                'person': {
 | 
			
		||||
                    '_id': p1.pk,
 | 
			
		||||
                    'group': g1.pk
 | 
			
		||||
                }
 | 
			
		||||
        })
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            SocialData.objects._collection.find_one({"tags": "tag2"}),
 | 
			
		||||
            {
 | 
			
		||||
                "_id": s1.pk,
 | 
			
		||||
                "obs": "testing 123",
 | 
			
		||||
                "tags": ["tag1", "tag2"],
 | 
			
		||||
                "person": {"_id": p1.pk, "group": g1.pk},
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(SocialData.objects(person__group=g2).count(), 1)
 | 
			
		||||
        self.assertEqual(SocialData.objects(person__group=g2).first(), s2)
 | 
			
		||||
@@ -163,23 +151,18 @@ class TestCachedReferenceField(MongoDBTestCase):
 | 
			
		||||
        Product.drop_collection()
 | 
			
		||||
 | 
			
		||||
        class Basket(Document):
 | 
			
		||||
            products = ListField(CachedReferenceField(Product, fields=['name']))
 | 
			
		||||
            products = ListField(CachedReferenceField(Product, fields=["name"]))
 | 
			
		||||
 | 
			
		||||
        Basket.drop_collection()
 | 
			
		||||
        product1 = Product(name='abc').save()
 | 
			
		||||
        product2 = Product(name='def').save()
 | 
			
		||||
        product1 = Product(name="abc").save()
 | 
			
		||||
        product2 = Product(name="def").save()
 | 
			
		||||
        basket = Basket(products=[product1]).save()
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            Basket.objects._collection.find_one(),
 | 
			
		||||
            {
 | 
			
		||||
                '_id': basket.pk,
 | 
			
		||||
                'products': [
 | 
			
		||||
                    {
 | 
			
		||||
                        '_id': product1.pk,
 | 
			
		||||
                        'name': product1.name
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            }
 | 
			
		||||
                "_id": basket.pk,
 | 
			
		||||
                "products": [{"_id": product1.pk, "name": product1.name}],
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
        # push to list
 | 
			
		||||
        basket.update(push__products=product2)
 | 
			
		||||
@@ -187,161 +170,135 @@ class TestCachedReferenceField(MongoDBTestCase):
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            Basket.objects._collection.find_one(),
 | 
			
		||||
            {
 | 
			
		||||
                '_id': basket.pk,
 | 
			
		||||
                'products': [
 | 
			
		||||
                    {
 | 
			
		||||
                        '_id': product1.pk,
 | 
			
		||||
                        'name': product1.name
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        '_id': product2.pk,
 | 
			
		||||
                        'name': product2.name
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            }
 | 
			
		||||
                "_id": basket.pk,
 | 
			
		||||
                "products": [
 | 
			
		||||
                    {"_id": product1.pk, "name": product1.name},
 | 
			
		||||
                    {"_id": product2.pk, "name": product2.name},
 | 
			
		||||
                ],
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_cached_reference_field_update_all(self):
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            TYPES = (
 | 
			
		||||
                ('pf', "PF"),
 | 
			
		||||
                ('pj', "PJ")
 | 
			
		||||
            )
 | 
			
		||||
            TYPES = (("pf", "PF"), ("pj", "PJ"))
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            tp = StringField(choices=TYPES)
 | 
			
		||||
            father = CachedReferenceField('self', fields=('tp',))
 | 
			
		||||
            father = CachedReferenceField("self", fields=("tp",))
 | 
			
		||||
 | 
			
		||||
        Person.drop_collection()
 | 
			
		||||
 | 
			
		||||
        a1 = Person(name="Wilson Father", tp="pj")
 | 
			
		||||
        a1.save()
 | 
			
		||||
 | 
			
		||||
        a2 = Person(name='Wilson Junior', tp='pf', father=a1)
 | 
			
		||||
        a2 = Person(name="Wilson Junior", tp="pf", father=a1)
 | 
			
		||||
        a2.save()
 | 
			
		||||
 | 
			
		||||
        a2 = Person.objects.with_id(a2.id)
 | 
			
		||||
        self.assertEqual(a2.father.tp, a1.tp)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(dict(a2.to_mongo()), {
 | 
			
		||||
            "_id": a2.pk,
 | 
			
		||||
            "name": u"Wilson Junior",
 | 
			
		||||
            "tp": u"pf",
 | 
			
		||||
            "father": {
 | 
			
		||||
                "_id": a1.pk,
 | 
			
		||||
                "tp": u"pj"
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            dict(a2.to_mongo()),
 | 
			
		||||
            {
 | 
			
		||||
                "_id": a2.pk,
 | 
			
		||||
                "name": u"Wilson Junior",
 | 
			
		||||
                "tp": u"pf",
 | 
			
		||||
                "father": {"_id": a1.pk, "tp": u"pj"},
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(Person.objects(father=a1)._query, {
 | 
			
		||||
            'father._id': a1.pk
 | 
			
		||||
        })
 | 
			
		||||
        self.assertEqual(Person.objects(father=a1)._query, {"father._id": a1.pk})
 | 
			
		||||
        self.assertEqual(Person.objects(father=a1).count(), 1)
 | 
			
		||||
 | 
			
		||||
        Person.objects.update(set__tp="pf")
 | 
			
		||||
        Person.father.sync_all()
 | 
			
		||||
 | 
			
		||||
        a2.reload()
 | 
			
		||||
        self.assertEqual(dict(a2.to_mongo()), {
 | 
			
		||||
            "_id": a2.pk,
 | 
			
		||||
            "name": u"Wilson Junior",
 | 
			
		||||
            "tp": u"pf",
 | 
			
		||||
            "father": {
 | 
			
		||||
                "_id": a1.pk,
 | 
			
		||||
                "tp": u"pf"
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            dict(a2.to_mongo()),
 | 
			
		||||
            {
 | 
			
		||||
                "_id": a2.pk,
 | 
			
		||||
                "name": u"Wilson Junior",
 | 
			
		||||
                "tp": u"pf",
 | 
			
		||||
                "father": {"_id": a1.pk, "tp": u"pf"},
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_cached_reference_fields_on_embedded_documents(self):
 | 
			
		||||
        with self.assertRaises(InvalidDocumentError):
 | 
			
		||||
 | 
			
		||||
            class Test(Document):
 | 
			
		||||
                name = StringField()
 | 
			
		||||
 | 
			
		||||
            type('WrongEmbeddedDocument', (
 | 
			
		||||
                EmbeddedDocument,), {
 | 
			
		||||
                    'test': CachedReferenceField(Test)
 | 
			
		||||
            })
 | 
			
		||||
            type(
 | 
			
		||||
                "WrongEmbeddedDocument",
 | 
			
		||||
                (EmbeddedDocument,),
 | 
			
		||||
                {"test": CachedReferenceField(Test)},
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    def test_cached_reference_auto_sync(self):
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            TYPES = (
 | 
			
		||||
                ('pf', "PF"),
 | 
			
		||||
                ('pj', "PJ")
 | 
			
		||||
            )
 | 
			
		||||
            TYPES = (("pf", "PF"), ("pj", "PJ"))
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            tp = StringField(
 | 
			
		||||
                choices=TYPES
 | 
			
		||||
            )
 | 
			
		||||
            tp = StringField(choices=TYPES)
 | 
			
		||||
 | 
			
		||||
            father = CachedReferenceField('self', fields=('tp',))
 | 
			
		||||
            father = CachedReferenceField("self", fields=("tp",))
 | 
			
		||||
 | 
			
		||||
        Person.drop_collection()
 | 
			
		||||
 | 
			
		||||
        a1 = Person(name="Wilson Father", tp="pj")
 | 
			
		||||
        a1.save()
 | 
			
		||||
 | 
			
		||||
        a2 = Person(name='Wilson Junior', tp='pf', father=a1)
 | 
			
		||||
        a2 = Person(name="Wilson Junior", tp="pf", father=a1)
 | 
			
		||||
        a2.save()
 | 
			
		||||
 | 
			
		||||
        a1.tp = 'pf'
 | 
			
		||||
        a1.tp = "pf"
 | 
			
		||||
        a1.save()
 | 
			
		||||
 | 
			
		||||
        a2.reload()
 | 
			
		||||
        self.assertEqual(dict(a2.to_mongo()), {
 | 
			
		||||
            '_id': a2.pk,
 | 
			
		||||
            'name': 'Wilson Junior',
 | 
			
		||||
            'tp': 'pf',
 | 
			
		||||
            'father': {
 | 
			
		||||
                '_id': a1.pk,
 | 
			
		||||
                'tp': 'pf'
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            dict(a2.to_mongo()),
 | 
			
		||||
            {
 | 
			
		||||
                "_id": a2.pk,
 | 
			
		||||
                "name": "Wilson Junior",
 | 
			
		||||
                "tp": "pf",
 | 
			
		||||
                "father": {"_id": a1.pk, "tp": "pf"},
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_cached_reference_auto_sync_disabled(self):
 | 
			
		||||
        class Persone(Document):
 | 
			
		||||
            TYPES = (
 | 
			
		||||
                ('pf', "PF"),
 | 
			
		||||
                ('pj', "PJ")
 | 
			
		||||
            )
 | 
			
		||||
            TYPES = (("pf", "PF"), ("pj", "PJ"))
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            tp = StringField(
 | 
			
		||||
                choices=TYPES
 | 
			
		||||
            )
 | 
			
		||||
            tp = StringField(choices=TYPES)
 | 
			
		||||
 | 
			
		||||
            father = CachedReferenceField(
 | 
			
		||||
                'self', fields=('tp',), auto_sync=False)
 | 
			
		||||
            father = CachedReferenceField("self", fields=("tp",), auto_sync=False)
 | 
			
		||||
 | 
			
		||||
        Persone.drop_collection()
 | 
			
		||||
 | 
			
		||||
        a1 = Persone(name="Wilson Father", tp="pj")
 | 
			
		||||
        a1.save()
 | 
			
		||||
 | 
			
		||||
        a2 = Persone(name='Wilson Junior', tp='pf', father=a1)
 | 
			
		||||
        a2 = Persone(name="Wilson Junior", tp="pf", father=a1)
 | 
			
		||||
        a2.save()
 | 
			
		||||
 | 
			
		||||
        a1.tp = 'pf'
 | 
			
		||||
        a1.tp = "pf"
 | 
			
		||||
        a1.save()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(Persone.objects._collection.find_one({'_id': a2.pk}), {
 | 
			
		||||
            '_id': a2.pk,
 | 
			
		||||
            'name': 'Wilson Junior',
 | 
			
		||||
            'tp': 'pf',
 | 
			
		||||
            'father': {
 | 
			
		||||
                '_id': a1.pk,
 | 
			
		||||
                'tp': 'pj'
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            Persone.objects._collection.find_one({"_id": a2.pk}),
 | 
			
		||||
            {
 | 
			
		||||
                "_id": a2.pk,
 | 
			
		||||
                "name": "Wilson Junior",
 | 
			
		||||
                "tp": "pf",
 | 
			
		||||
                "father": {"_id": a1.pk, "tp": "pj"},
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_cached_reference_embedded_fields(self):
 | 
			
		||||
        class Owner(EmbeddedDocument):
 | 
			
		||||
            TPS = (
 | 
			
		||||
                ('n', "Normal"),
 | 
			
		||||
                ('u', "Urgent")
 | 
			
		||||
            )
 | 
			
		||||
            TPS = (("n", "Normal"), ("u", "Urgent"))
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            tp = StringField(
 | 
			
		||||
                verbose_name="Type",
 | 
			
		||||
                db_field="t",
 | 
			
		||||
                choices=TPS)
 | 
			
		||||
            tp = StringField(verbose_name="Type", db_field="t", choices=TPS)
 | 
			
		||||
 | 
			
		||||
        class Animal(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
@@ -351,43 +308,38 @@ class TestCachedReferenceField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        class Ocorrence(Document):
 | 
			
		||||
            person = StringField()
 | 
			
		||||
            animal = CachedReferenceField(
 | 
			
		||||
                Animal, fields=['tag', 'owner.tp'])
 | 
			
		||||
            animal = CachedReferenceField(Animal, fields=["tag", "owner.tp"])
 | 
			
		||||
 | 
			
		||||
        Animal.drop_collection()
 | 
			
		||||
        Ocorrence.drop_collection()
 | 
			
		||||
 | 
			
		||||
        a = Animal(name="Leopard", tag="heavy",
 | 
			
		||||
                   owner=Owner(tp='u', name="Wilson Júnior")
 | 
			
		||||
                   )
 | 
			
		||||
        a = Animal(
 | 
			
		||||
            name="Leopard", tag="heavy", owner=Owner(tp="u", name="Wilson Júnior")
 | 
			
		||||
        )
 | 
			
		||||
        a.save()
 | 
			
		||||
 | 
			
		||||
        o = Ocorrence(person="teste", animal=a)
 | 
			
		||||
        o.save()
 | 
			
		||||
        self.assertEqual(dict(a.to_mongo(fields=['tag', 'owner.tp'])), {
 | 
			
		||||
            '_id': a.pk,
 | 
			
		||||
            'tag': 'heavy',
 | 
			
		||||
            'owner': {
 | 
			
		||||
                't': 'u'
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        self.assertEqual(o.to_mongo()['animal']['tag'], 'heavy')
 | 
			
		||||
        self.assertEqual(o.to_mongo()['animal']['owner']['t'], 'u')
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            dict(a.to_mongo(fields=["tag", "owner.tp"])),
 | 
			
		||||
            {"_id": a.pk, "tag": "heavy", "owner": {"t": "u"}},
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(o.to_mongo()["animal"]["tag"], "heavy")
 | 
			
		||||
        self.assertEqual(o.to_mongo()["animal"]["owner"]["t"], "u")
 | 
			
		||||
 | 
			
		||||
        # Check to_mongo with fields
 | 
			
		||||
        self.assertNotIn('animal', o.to_mongo(fields=['person']))
 | 
			
		||||
        self.assertNotIn("animal", o.to_mongo(fields=["person"]))
 | 
			
		||||
 | 
			
		||||
        # counts
 | 
			
		||||
        Ocorrence(person="teste 2").save()
 | 
			
		||||
        Ocorrence(person="teste 3").save()
 | 
			
		||||
 | 
			
		||||
        count = Ocorrence.objects(
 | 
			
		||||
            animal__tag='heavy', animal__owner__tp='u').count()
 | 
			
		||||
        count = Ocorrence.objects(animal__tag="heavy", animal__owner__tp="u").count()
 | 
			
		||||
        self.assertEqual(count, 1)
 | 
			
		||||
 | 
			
		||||
        ocorrence = Ocorrence.objects(
 | 
			
		||||
            animal__tag='heavy',
 | 
			
		||||
            animal__owner__tp='u').first()
 | 
			
		||||
            animal__tag="heavy", animal__owner__tp="u"
 | 
			
		||||
        ).first()
 | 
			
		||||
        self.assertEqual(ocorrence.person, "teste")
 | 
			
		||||
        self.assertIsInstance(ocorrence.animal, Animal)
 | 
			
		||||
 | 
			
		||||
@@ -404,43 +356,39 @@ class TestCachedReferenceField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        class Ocorrence(Document):
 | 
			
		||||
            person = StringField()
 | 
			
		||||
            animal = CachedReferenceField(
 | 
			
		||||
                Animal, fields=['tag', 'owner.tags'])
 | 
			
		||||
            animal = CachedReferenceField(Animal, fields=["tag", "owner.tags"])
 | 
			
		||||
 | 
			
		||||
        Animal.drop_collection()
 | 
			
		||||
        Ocorrence.drop_collection()
 | 
			
		||||
 | 
			
		||||
        a = Animal(name="Leopard", tag="heavy",
 | 
			
		||||
                   owner=Owner(tags=['cool', 'funny'],
 | 
			
		||||
                               name="Wilson Júnior")
 | 
			
		||||
                   )
 | 
			
		||||
        a = Animal(
 | 
			
		||||
            name="Leopard",
 | 
			
		||||
            tag="heavy",
 | 
			
		||||
            owner=Owner(tags=["cool", "funny"], name="Wilson Júnior"),
 | 
			
		||||
        )
 | 
			
		||||
        a.save()
 | 
			
		||||
 | 
			
		||||
        o = Ocorrence(person="teste 2", animal=a)
 | 
			
		||||
        o.save()
 | 
			
		||||
        self.assertEqual(dict(a.to_mongo(fields=['tag', 'owner.tags'])), {
 | 
			
		||||
            '_id': a.pk,
 | 
			
		||||
            'tag': 'heavy',
 | 
			
		||||
            'owner': {
 | 
			
		||||
                'tags': ['cool', 'funny']
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            dict(a.to_mongo(fields=["tag", "owner.tags"])),
 | 
			
		||||
            {"_id": a.pk, "tag": "heavy", "owner": {"tags": ["cool", "funny"]}},
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(o.to_mongo()['animal']['tag'], 'heavy')
 | 
			
		||||
        self.assertEqual(o.to_mongo()['animal']['owner']['tags'],
 | 
			
		||||
                         ['cool', 'funny'])
 | 
			
		||||
        self.assertEqual(o.to_mongo()["animal"]["tag"], "heavy")
 | 
			
		||||
        self.assertEqual(o.to_mongo()["animal"]["owner"]["tags"], ["cool", "funny"])
 | 
			
		||||
 | 
			
		||||
        # counts
 | 
			
		||||
        Ocorrence(person="teste 2").save()
 | 
			
		||||
        Ocorrence(person="teste 3").save()
 | 
			
		||||
 | 
			
		||||
        query = Ocorrence.objects(
 | 
			
		||||
            animal__tag='heavy', animal__owner__tags='cool')._query
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            query, {'animal.owner.tags': 'cool', 'animal.tag': 'heavy'})
 | 
			
		||||
            animal__tag="heavy", animal__owner__tags="cool"
 | 
			
		||||
        )._query
 | 
			
		||||
        self.assertEqual(query, {"animal.owner.tags": "cool", "animal.tag": "heavy"})
 | 
			
		||||
 | 
			
		||||
        ocorrence = Ocorrence.objects(
 | 
			
		||||
            animal__tag='heavy',
 | 
			
		||||
            animal__owner__tags='cool').first()
 | 
			
		||||
            animal__tag="heavy", animal__owner__tags="cool"
 | 
			
		||||
        ).first()
 | 
			
		||||
        self.assertEqual(ocorrence.person, "teste 2")
 | 
			
		||||
        self.assertIsInstance(ocorrence.animal, Animal)
 | 
			
		||||
 
 | 
			
		||||
@@ -14,9 +14,10 @@ class ComplexDateTimeFieldTest(MongoDBTestCase):
 | 
			
		||||
        """Tests for complex datetime fields - which can handle
 | 
			
		||||
        microseconds without rounding.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class LogEntry(Document):
 | 
			
		||||
            date = ComplexDateTimeField()
 | 
			
		||||
            date_with_dots = ComplexDateTimeField(separator='.')
 | 
			
		||||
            date_with_dots = ComplexDateTimeField(separator=".")
 | 
			
		||||
 | 
			
		||||
        LogEntry.drop_collection()
 | 
			
		||||
 | 
			
		||||
@@ -62,17 +63,25 @@ class ComplexDateTimeFieldTest(MongoDBTestCase):
 | 
			
		||||
        mm = dd = hh = ii = ss = [1, 10]
 | 
			
		||||
 | 
			
		||||
        for values in itertools.product([2014], mm, dd, hh, ii, ss, microsecond):
 | 
			
		||||
            stored = LogEntry(date=datetime.datetime(*values)).to_mongo()['date']
 | 
			
		||||
            self.assertTrue(re.match('^\d{4},\d{2},\d{2},\d{2},\d{2},\d{2},\d{6}$', stored) is not None)
 | 
			
		||||
            stored = LogEntry(date=datetime.datetime(*values)).to_mongo()["date"]
 | 
			
		||||
            self.assertTrue(
 | 
			
		||||
                re.match("^\d{4},\d{2},\d{2},\d{2},\d{2},\d{2},\d{6}$", stored)
 | 
			
		||||
                is not None
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        # Test separator
 | 
			
		||||
        stored = LogEntry(date_with_dots=datetime.datetime(2014, 1, 1)).to_mongo()['date_with_dots']
 | 
			
		||||
        self.assertTrue(re.match('^\d{4}.\d{2}.\d{2}.\d{2}.\d{2}.\d{2}.\d{6}$', stored) is not None)
 | 
			
		||||
        stored = LogEntry(date_with_dots=datetime.datetime(2014, 1, 1)).to_mongo()[
 | 
			
		||||
            "date_with_dots"
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertTrue(
 | 
			
		||||
            re.match("^\d{4}.\d{2}.\d{2}.\d{2}.\d{2}.\d{2}.\d{6}$", stored) is not None
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_complexdatetime_usage(self):
 | 
			
		||||
        """Tests for complex datetime fields - which can handle
 | 
			
		||||
        microseconds without rounding.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class LogEntry(Document):
 | 
			
		||||
            date = ComplexDateTimeField()
 | 
			
		||||
 | 
			
		||||
@@ -123,22 +132,21 @@ class ComplexDateTimeFieldTest(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        # Test microsecond-level ordering/filtering
 | 
			
		||||
        for microsecond in (99, 999, 9999, 10000):
 | 
			
		||||
            LogEntry(
 | 
			
		||||
                date=datetime.datetime(2015, 1, 1, 0, 0, 0, microsecond)
 | 
			
		||||
            ).save()
 | 
			
		||||
            LogEntry(date=datetime.datetime(2015, 1, 1, 0, 0, 0, microsecond)).save()
 | 
			
		||||
 | 
			
		||||
        logs = list(LogEntry.objects.order_by('date'))
 | 
			
		||||
        logs = list(LogEntry.objects.order_by("date"))
 | 
			
		||||
        for next_idx, log in enumerate(logs[:-1], start=1):
 | 
			
		||||
            next_log = logs[next_idx]
 | 
			
		||||
            self.assertTrue(log.date < next_log.date)
 | 
			
		||||
 | 
			
		||||
        logs = list(LogEntry.objects.order_by('-date'))
 | 
			
		||||
        logs = list(LogEntry.objects.order_by("-date"))
 | 
			
		||||
        for next_idx, log in enumerate(logs[:-1], start=1):
 | 
			
		||||
            next_log = logs[next_idx]
 | 
			
		||||
            self.assertTrue(log.date > next_log.date)
 | 
			
		||||
 | 
			
		||||
        logs = LogEntry.objects.filter(
 | 
			
		||||
            date__lte=datetime.datetime(2015, 1, 1, 0, 0, 0, 10000))
 | 
			
		||||
            date__lte=datetime.datetime(2015, 1, 1, 0, 0, 0, 10000)
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(logs.count(), 4)
 | 
			
		||||
 | 
			
		||||
    def test_no_default_value(self):
 | 
			
		||||
@@ -156,6 +164,7 @@ class ComplexDateTimeFieldTest(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_default_static_value(self):
 | 
			
		||||
        NOW = datetime.datetime.utcnow()
 | 
			
		||||
 | 
			
		||||
        class Log(Document):
 | 
			
		||||
            timestamp = ComplexDateTimeField(default=NOW)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -18,10 +18,11 @@ class TestDateField(MongoDBTestCase):
 | 
			
		||||
        Ensure an exception is raised when trying to
 | 
			
		||||
        cast an empty string to datetime.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class MyDoc(Document):
 | 
			
		||||
            dt = DateField()
 | 
			
		||||
 | 
			
		||||
        md = MyDoc(dt='')
 | 
			
		||||
        md = MyDoc(dt="")
 | 
			
		||||
        self.assertRaises(ValidationError, md.save)
 | 
			
		||||
 | 
			
		||||
    def test_date_from_whitespace_string(self):
 | 
			
		||||
@@ -29,16 +30,18 @@ class TestDateField(MongoDBTestCase):
 | 
			
		||||
        Ensure an exception is raised when trying to
 | 
			
		||||
        cast a whitespace-only string to datetime.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class MyDoc(Document):
 | 
			
		||||
            dt = DateField()
 | 
			
		||||
 | 
			
		||||
        md = MyDoc(dt='   ')
 | 
			
		||||
        md = MyDoc(dt="   ")
 | 
			
		||||
        self.assertRaises(ValidationError, md.save)
 | 
			
		||||
 | 
			
		||||
    def test_default_values_today(self):
 | 
			
		||||
        """Ensure that default field values are used when creating
 | 
			
		||||
        a document.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            day = DateField(default=datetime.date.today)
 | 
			
		||||
 | 
			
		||||
@@ -46,13 +49,14 @@ class TestDateField(MongoDBTestCase):
 | 
			
		||||
        person.validate()
 | 
			
		||||
        self.assertEqual(person.day, person.day)
 | 
			
		||||
        self.assertEqual(person.day, datetime.date.today())
 | 
			
		||||
        self.assertEqual(person._data['day'], person.day)
 | 
			
		||||
        self.assertEqual(person._data["day"], person.day)
 | 
			
		||||
 | 
			
		||||
    def test_date(self):
 | 
			
		||||
        """Tests showing pymongo date fields
 | 
			
		||||
 | 
			
		||||
        See: http://api.mongodb.org/python/current/api/bson/son.html#dt
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class LogEntry(Document):
 | 
			
		||||
            date = DateField()
 | 
			
		||||
 | 
			
		||||
@@ -95,6 +99,7 @@ class TestDateField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_regular_usage(self):
 | 
			
		||||
        """Tests for regular datetime fields"""
 | 
			
		||||
 | 
			
		||||
        class LogEntry(Document):
 | 
			
		||||
            date = DateField()
 | 
			
		||||
 | 
			
		||||
@@ -106,12 +111,12 @@ class TestDateField(MongoDBTestCase):
 | 
			
		||||
        log.validate()
 | 
			
		||||
        log.save()
 | 
			
		||||
 | 
			
		||||
        for query in (d1, d1.isoformat(' ')):
 | 
			
		||||
        for query in (d1, d1.isoformat(" ")):
 | 
			
		||||
            log1 = LogEntry.objects.get(date=query)
 | 
			
		||||
            self.assertEqual(log, log1)
 | 
			
		||||
 | 
			
		||||
        if dateutil:
 | 
			
		||||
            log1 = LogEntry.objects.get(date=d1.isoformat('T'))
 | 
			
		||||
            log1 = LogEntry.objects.get(date=d1.isoformat("T"))
 | 
			
		||||
            self.assertEqual(log, log1)
 | 
			
		||||
 | 
			
		||||
        # create additional 19 log entries for a total of 20
 | 
			
		||||
@@ -142,6 +147,7 @@ class TestDateField(MongoDBTestCase):
 | 
			
		||||
        """Ensure that invalid values cannot be assigned to datetime
 | 
			
		||||
        fields.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class LogEntry(Document):
 | 
			
		||||
            time = DateField()
 | 
			
		||||
 | 
			
		||||
@@ -152,14 +158,14 @@ class TestDateField(MongoDBTestCase):
 | 
			
		||||
        log.time = datetime.date.today()
 | 
			
		||||
        log.validate()
 | 
			
		||||
 | 
			
		||||
        log.time = datetime.datetime.now().isoformat(' ')
 | 
			
		||||
        log.time = datetime.datetime.now().isoformat(" ")
 | 
			
		||||
        log.validate()
 | 
			
		||||
 | 
			
		||||
        if dateutil:
 | 
			
		||||
            log.time = datetime.datetime.now().isoformat('T')
 | 
			
		||||
            log.time = datetime.datetime.now().isoformat("T")
 | 
			
		||||
            log.validate()
 | 
			
		||||
 | 
			
		||||
        log.time = -1
 | 
			
		||||
        self.assertRaises(ValidationError, log.validate)
 | 
			
		||||
        log.time = 'ABC'
 | 
			
		||||
        log.time = "ABC"
 | 
			
		||||
        self.assertRaises(ValidationError, log.validate)
 | 
			
		||||
 
 | 
			
		||||
@@ -19,10 +19,11 @@ class TestDateTimeField(MongoDBTestCase):
 | 
			
		||||
        Ensure an exception is raised when trying to
 | 
			
		||||
        cast an empty string to datetime.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class MyDoc(Document):
 | 
			
		||||
            dt = DateTimeField()
 | 
			
		||||
 | 
			
		||||
        md = MyDoc(dt='')
 | 
			
		||||
        md = MyDoc(dt="")
 | 
			
		||||
        self.assertRaises(ValidationError, md.save)
 | 
			
		||||
 | 
			
		||||
    def test_datetime_from_whitespace_string(self):
 | 
			
		||||
@@ -30,16 +31,18 @@ class TestDateTimeField(MongoDBTestCase):
 | 
			
		||||
        Ensure an exception is raised when trying to
 | 
			
		||||
        cast a whitespace-only string to datetime.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class MyDoc(Document):
 | 
			
		||||
            dt = DateTimeField()
 | 
			
		||||
 | 
			
		||||
        md = MyDoc(dt='   ')
 | 
			
		||||
        md = MyDoc(dt="   ")
 | 
			
		||||
        self.assertRaises(ValidationError, md.save)
 | 
			
		||||
 | 
			
		||||
    def test_default_value_utcnow(self):
 | 
			
		||||
        """Ensure that default field values are used when creating
 | 
			
		||||
        a document.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            created = DateTimeField(default=dt.datetime.utcnow)
 | 
			
		||||
 | 
			
		||||
@@ -48,8 +51,10 @@ class TestDateTimeField(MongoDBTestCase):
 | 
			
		||||
        person.validate()
 | 
			
		||||
        person_created_t0 = person.created
 | 
			
		||||
        self.assertLess(person.created - utcnow, dt.timedelta(seconds=1))
 | 
			
		||||
        self.assertEqual(person_created_t0, person.created)  # make sure it does not change
 | 
			
		||||
        self.assertEqual(person._data['created'], person.created)
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            person_created_t0, person.created
 | 
			
		||||
        )  # make sure it does not change
 | 
			
		||||
        self.assertEqual(person._data["created"], person.created)
 | 
			
		||||
 | 
			
		||||
    def test_handling_microseconds(self):
 | 
			
		||||
        """Tests showing pymongo datetime fields handling of microseconds.
 | 
			
		||||
@@ -58,6 +63,7 @@ class TestDateTimeField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        See: http://api.mongodb.org/python/current/api/bson/son.html#dt
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class LogEntry(Document):
 | 
			
		||||
            date = DateTimeField()
 | 
			
		||||
 | 
			
		||||
@@ -103,6 +109,7 @@ class TestDateTimeField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_regular_usage(self):
 | 
			
		||||
        """Tests for regular datetime fields"""
 | 
			
		||||
 | 
			
		||||
        class LogEntry(Document):
 | 
			
		||||
            date = DateTimeField()
 | 
			
		||||
 | 
			
		||||
@@ -114,12 +121,12 @@ class TestDateTimeField(MongoDBTestCase):
 | 
			
		||||
        log.validate()
 | 
			
		||||
        log.save()
 | 
			
		||||
 | 
			
		||||
        for query in (d1, d1.isoformat(' ')):
 | 
			
		||||
        for query in (d1, d1.isoformat(" ")):
 | 
			
		||||
            log1 = LogEntry.objects.get(date=query)
 | 
			
		||||
            self.assertEqual(log, log1)
 | 
			
		||||
 | 
			
		||||
        if dateutil:
 | 
			
		||||
            log1 = LogEntry.objects.get(date=d1.isoformat('T'))
 | 
			
		||||
            log1 = LogEntry.objects.get(date=d1.isoformat("T"))
 | 
			
		||||
            self.assertEqual(log, log1)
 | 
			
		||||
 | 
			
		||||
        # create additional 19 log entries for a total of 20
 | 
			
		||||
@@ -150,8 +157,7 @@ class TestDateTimeField(MongoDBTestCase):
 | 
			
		||||
        self.assertEqual(logs.count(), 10)
 | 
			
		||||
 | 
			
		||||
        logs = LogEntry.objects.filter(
 | 
			
		||||
            date__lte=dt.datetime(1980, 1, 1),
 | 
			
		||||
            date__gte=dt.datetime(1975, 1, 1),
 | 
			
		||||
            date__lte=dt.datetime(1980, 1, 1), date__gte=dt.datetime(1975, 1, 1)
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(logs.count(), 5)
 | 
			
		||||
 | 
			
		||||
@@ -159,6 +165,7 @@ class TestDateTimeField(MongoDBTestCase):
 | 
			
		||||
        """Ensure that invalid values cannot be assigned to datetime
 | 
			
		||||
        fields.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class LogEntry(Document):
 | 
			
		||||
            time = DateTimeField()
 | 
			
		||||
 | 
			
		||||
@@ -169,32 +176,32 @@ class TestDateTimeField(MongoDBTestCase):
 | 
			
		||||
        log.time = dt.date.today()
 | 
			
		||||
        log.validate()
 | 
			
		||||
 | 
			
		||||
        log.time = dt.datetime.now().isoformat(' ')
 | 
			
		||||
        log.time = dt.datetime.now().isoformat(" ")
 | 
			
		||||
        log.validate()
 | 
			
		||||
 | 
			
		||||
        log.time = '2019-05-16 21:42:57.897847'
 | 
			
		||||
        log.time = "2019-05-16 21:42:57.897847"
 | 
			
		||||
        log.validate()
 | 
			
		||||
 | 
			
		||||
        if dateutil:
 | 
			
		||||
            log.time = dt.datetime.now().isoformat('T')
 | 
			
		||||
            log.time = dt.datetime.now().isoformat("T")
 | 
			
		||||
            log.validate()
 | 
			
		||||
 | 
			
		||||
        log.time = -1
 | 
			
		||||
        self.assertRaises(ValidationError, log.validate)
 | 
			
		||||
        log.time = 'ABC'
 | 
			
		||||
        log.time = "ABC"
 | 
			
		||||
        self.assertRaises(ValidationError, log.validate)
 | 
			
		||||
        log.time = '2019-05-16 21:GARBAGE:12'
 | 
			
		||||
        log.time = "2019-05-16 21:GARBAGE:12"
 | 
			
		||||
        self.assertRaises(ValidationError, log.validate)
 | 
			
		||||
        log.time = '2019-05-16 21:42:57.GARBAGE'
 | 
			
		||||
        log.time = "2019-05-16 21:42:57.GARBAGE"
 | 
			
		||||
        self.assertRaises(ValidationError, log.validate)
 | 
			
		||||
        log.time = '2019-05-16 21:42:57.123.456'
 | 
			
		||||
        log.time = "2019-05-16 21:42:57.123.456"
 | 
			
		||||
        self.assertRaises(ValidationError, log.validate)
 | 
			
		||||
 | 
			
		||||
    def test_parse_datetime_as_str(self):
 | 
			
		||||
        class DTDoc(Document):
 | 
			
		||||
            date = DateTimeField()
 | 
			
		||||
 | 
			
		||||
        date_str = '2019-03-02 22:26:01'
 | 
			
		||||
        date_str = "2019-03-02 22:26:01"
 | 
			
		||||
 | 
			
		||||
        # make sure that passing a parsable datetime works
 | 
			
		||||
        dtd = DTDoc()
 | 
			
		||||
@@ -206,7 +213,7 @@ class TestDateTimeField(MongoDBTestCase):
 | 
			
		||||
        self.assertIsInstance(dtd.date, dt.datetime)
 | 
			
		||||
        self.assertEqual(str(dtd.date), date_str)
 | 
			
		||||
 | 
			
		||||
        dtd.date = 'January 1st, 9999999999'
 | 
			
		||||
        dtd.date = "January 1st, 9999999999"
 | 
			
		||||
        self.assertRaises(ValidationError, dtd.validate)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -217,7 +224,7 @@ class TestDateTimeTzAware(MongoDBTestCase):
 | 
			
		||||
        connection._connections = {}
 | 
			
		||||
        connection._dbs = {}
 | 
			
		||||
 | 
			
		||||
        connect(db='mongoenginetest', tz_aware=True)
 | 
			
		||||
        connect(db="mongoenginetest", tz_aware=True)
 | 
			
		||||
 | 
			
		||||
        class LogEntry(Document):
 | 
			
		||||
            time = DateTimeField()
 | 
			
		||||
@@ -228,4 +235,4 @@ class TestDateTimeTzAware(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        log = LogEntry.objects.first()
 | 
			
		||||
        log.time = dt.datetime(2013, 1, 1, 0, 0, 0)
 | 
			
		||||
        self.assertEqual(['time'], log._changed_fields)
 | 
			
		||||
        self.assertEqual(["time"], log._changed_fields)
 | 
			
		||||
 
 | 
			
		||||
@@ -7,32 +7,31 @@ from tests.utils import MongoDBTestCase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestDecimalField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_validation(self):
 | 
			
		||||
        """Ensure that invalid values cannot be assigned to decimal fields.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            height = DecimalField(min_value=Decimal('0.1'),
 | 
			
		||||
                                  max_value=Decimal('3.5'))
 | 
			
		||||
            height = DecimalField(min_value=Decimal("0.1"), max_value=Decimal("3.5"))
 | 
			
		||||
 | 
			
		||||
        Person.drop_collection()
 | 
			
		||||
 | 
			
		||||
        Person(height=Decimal('1.89')).save()
 | 
			
		||||
        Person(height=Decimal("1.89")).save()
 | 
			
		||||
        person = Person.objects.first()
 | 
			
		||||
        self.assertEqual(person.height, Decimal('1.89'))
 | 
			
		||||
        self.assertEqual(person.height, Decimal("1.89"))
 | 
			
		||||
 | 
			
		||||
        person.height = '2.0'
 | 
			
		||||
        person.height = "2.0"
 | 
			
		||||
        person.save()
 | 
			
		||||
        person.height = 0.01
 | 
			
		||||
        self.assertRaises(ValidationError, person.validate)
 | 
			
		||||
        person.height = Decimal('0.01')
 | 
			
		||||
        person.height = Decimal("0.01")
 | 
			
		||||
        self.assertRaises(ValidationError, person.validate)
 | 
			
		||||
        person.height = Decimal('4.0')
 | 
			
		||||
        person.height = Decimal("4.0")
 | 
			
		||||
        self.assertRaises(ValidationError, person.validate)
 | 
			
		||||
        person.height = 'something invalid'
 | 
			
		||||
        person.height = "something invalid"
 | 
			
		||||
        self.assertRaises(ValidationError, person.validate)
 | 
			
		||||
 | 
			
		||||
        person_2 = Person(height='something invalid')
 | 
			
		||||
        person_2 = Person(height="something invalid")
 | 
			
		||||
        self.assertRaises(ValidationError, person_2.validate)
 | 
			
		||||
 | 
			
		||||
    def test_comparison(self):
 | 
			
		||||
@@ -58,7 +57,14 @@ class TestDecimalField(MongoDBTestCase):
 | 
			
		||||
            string_value = DecimalField(precision=4, force_string=True)
 | 
			
		||||
 | 
			
		||||
        Person.drop_collection()
 | 
			
		||||
        values_to_store = [10, 10.1, 10.11, "10.111", Decimal("10.1111"), Decimal("10.11111")]
 | 
			
		||||
        values_to_store = [
 | 
			
		||||
            10,
 | 
			
		||||
            10.1,
 | 
			
		||||
            10.11,
 | 
			
		||||
            "10.111",
 | 
			
		||||
            Decimal("10.1111"),
 | 
			
		||||
            Decimal("10.11111"),
 | 
			
		||||
        ]
 | 
			
		||||
        for store_at_creation in [True, False]:
 | 
			
		||||
            for value in values_to_store:
 | 
			
		||||
                # to_python is called explicitly if values were sent in the kwargs of __init__
 | 
			
		||||
@@ -72,20 +78,27 @@ class TestDecimalField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        # How its stored
 | 
			
		||||
        expected = [
 | 
			
		||||
            {'float_value': 10.0, 'string_value': '10.0000'},
 | 
			
		||||
            {'float_value': 10.1, 'string_value': '10.1000'},
 | 
			
		||||
            {'float_value': 10.11, 'string_value': '10.1100'},
 | 
			
		||||
            {'float_value': 10.111, 'string_value': '10.1110'},
 | 
			
		||||
            {'float_value': 10.1111, 'string_value': '10.1111'},
 | 
			
		||||
            {'float_value': 10.1111, 'string_value': '10.1111'}]
 | 
			
		||||
            {"float_value": 10.0, "string_value": "10.0000"},
 | 
			
		||||
            {"float_value": 10.1, "string_value": "10.1000"},
 | 
			
		||||
            {"float_value": 10.11, "string_value": "10.1100"},
 | 
			
		||||
            {"float_value": 10.111, "string_value": "10.1110"},
 | 
			
		||||
            {"float_value": 10.1111, "string_value": "10.1111"},
 | 
			
		||||
            {"float_value": 10.1111, "string_value": "10.1111"},
 | 
			
		||||
        ]
 | 
			
		||||
        expected.extend(expected)
 | 
			
		||||
        actual = list(Person.objects.exclude('id').as_pymongo())
 | 
			
		||||
        actual = list(Person.objects.exclude("id").as_pymongo())
 | 
			
		||||
        self.assertEqual(expected, actual)
 | 
			
		||||
 | 
			
		||||
        # How it comes out locally
 | 
			
		||||
        expected = [Decimal('10.0000'), Decimal('10.1000'), Decimal('10.1100'),
 | 
			
		||||
                    Decimal('10.1110'), Decimal('10.1111'), Decimal('10.1111')]
 | 
			
		||||
        expected = [
 | 
			
		||||
            Decimal("10.0000"),
 | 
			
		||||
            Decimal("10.1000"),
 | 
			
		||||
            Decimal("10.1100"),
 | 
			
		||||
            Decimal("10.1110"),
 | 
			
		||||
            Decimal("10.1111"),
 | 
			
		||||
            Decimal("10.1111"),
 | 
			
		||||
        ]
 | 
			
		||||
        expected.extend(expected)
 | 
			
		||||
        for field_name in ['float_value', 'string_value']:
 | 
			
		||||
        for field_name in ["float_value", "string_value"]:
 | 
			
		||||
            actual = list(Person.objects().scalar(field_name))
 | 
			
		||||
            self.assertEqual(expected, actual)
 | 
			
		||||
 
 | 
			
		||||
@@ -6,95 +6,92 @@ from tests.utils import MongoDBTestCase, get_as_pymongo
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestDictField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_storage(self):
 | 
			
		||||
        class BlogPost(Document):
 | 
			
		||||
            info = DictField()
 | 
			
		||||
 | 
			
		||||
        BlogPost.drop_collection()
 | 
			
		||||
 | 
			
		||||
        info = {'testkey': 'testvalue'}
 | 
			
		||||
        info = {"testkey": "testvalue"}
 | 
			
		||||
        post = BlogPost(info=info).save()
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            get_as_pymongo(post),
 | 
			
		||||
            {
 | 
			
		||||
                '_id': post.id,
 | 
			
		||||
                'info': info
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(get_as_pymongo(post), {"_id": post.id, "info": info})
 | 
			
		||||
 | 
			
		||||
    def test_general_things(self):
 | 
			
		||||
        """Ensure that dict types work as expected."""
 | 
			
		||||
 | 
			
		||||
        class BlogPost(Document):
 | 
			
		||||
            info = DictField()
 | 
			
		||||
 | 
			
		||||
        BlogPost.drop_collection()
 | 
			
		||||
 | 
			
		||||
        post = BlogPost()
 | 
			
		||||
        post.info = 'my post'
 | 
			
		||||
        post.info = "my post"
 | 
			
		||||
        self.assertRaises(ValidationError, post.validate)
 | 
			
		||||
 | 
			
		||||
        post.info = ['test', 'test']
 | 
			
		||||
        post.info = ["test", "test"]
 | 
			
		||||
        self.assertRaises(ValidationError, post.validate)
 | 
			
		||||
 | 
			
		||||
        post.info = {'$title': 'test'}
 | 
			
		||||
        post.info = {"$title": "test"}
 | 
			
		||||
        self.assertRaises(ValidationError, post.validate)
 | 
			
		||||
 | 
			
		||||
        post.info = {'nested': {'$title': 'test'}}
 | 
			
		||||
        post.info = {"nested": {"$title": "test"}}
 | 
			
		||||
        self.assertRaises(ValidationError, post.validate)
 | 
			
		||||
 | 
			
		||||
        post.info = {'the.title': 'test'}
 | 
			
		||||
        post.info = {"the.title": "test"}
 | 
			
		||||
        self.assertRaises(ValidationError, post.validate)
 | 
			
		||||
 | 
			
		||||
        post.info = {'nested': {'the.title': 'test'}}
 | 
			
		||||
        post.info = {"nested": {"the.title": "test"}}
 | 
			
		||||
        self.assertRaises(ValidationError, post.validate)
 | 
			
		||||
 | 
			
		||||
        post.info = {1: 'test'}
 | 
			
		||||
        post.info = {1: "test"}
 | 
			
		||||
        self.assertRaises(ValidationError, post.validate)
 | 
			
		||||
 | 
			
		||||
        post.info = {'title': 'test'}
 | 
			
		||||
        post.info = {"title": "test"}
 | 
			
		||||
        post.save()
 | 
			
		||||
 | 
			
		||||
        post = BlogPost()
 | 
			
		||||
        post.info = {'title': 'dollar_sign', 'details': {'te$t': 'test'}}
 | 
			
		||||
        post.info = {"title": "dollar_sign", "details": {"te$t": "test"}}
 | 
			
		||||
        post.save()
 | 
			
		||||
 | 
			
		||||
        post = BlogPost()
 | 
			
		||||
        post.info = {'details': {'test': 'test'}}
 | 
			
		||||
        post.info = {"details": {"test": "test"}}
 | 
			
		||||
        post.save()
 | 
			
		||||
 | 
			
		||||
        post = BlogPost()
 | 
			
		||||
        post.info = {'details': {'test': 3}}
 | 
			
		||||
        post.info = {"details": {"test": 3}}
 | 
			
		||||
        post.save()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(BlogPost.objects.count(), 4)
 | 
			
		||||
        self.assertEqual(BlogPost.objects.filter(info__title__exact="test").count(), 1)
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            BlogPost.objects.filter(info__title__exact='test').count(), 1)
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            BlogPost.objects.filter(info__details__test__exact='test').count(), 1)
 | 
			
		||||
            BlogPost.objects.filter(info__details__test__exact="test").count(), 1
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        post = BlogPost.objects.filter(info__title__exact='dollar_sign').first()
 | 
			
		||||
        self.assertIn('te$t', post['info']['details'])
 | 
			
		||||
        post = BlogPost.objects.filter(info__title__exact="dollar_sign").first()
 | 
			
		||||
        self.assertIn("te$t", post["info"]["details"])
 | 
			
		||||
 | 
			
		||||
        # Confirm handles non strings or non existing keys
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            BlogPost.objects.filter(info__details__test__exact=5).count(), 0)
 | 
			
		||||
            BlogPost.objects.filter(info__details__test__exact=5).count(), 0
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            BlogPost.objects.filter(info__made_up__test__exact='test').count(), 0)
 | 
			
		||||
            BlogPost.objects.filter(info__made_up__test__exact="test").count(), 0
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        post = BlogPost.objects.create(info={'title': 'original'})
 | 
			
		||||
        post.info.update({'title': 'updated'})
 | 
			
		||||
        post = BlogPost.objects.create(info={"title": "original"})
 | 
			
		||||
        post.info.update({"title": "updated"})
 | 
			
		||||
        post.save()
 | 
			
		||||
        post.reload()
 | 
			
		||||
        self.assertEqual('updated', post.info['title'])
 | 
			
		||||
        self.assertEqual("updated", post.info["title"])
 | 
			
		||||
 | 
			
		||||
        post.info.setdefault('authors', [])
 | 
			
		||||
        post.info.setdefault("authors", [])
 | 
			
		||||
        post.save()
 | 
			
		||||
        post.reload()
 | 
			
		||||
        self.assertEqual([], post.info['authors'])
 | 
			
		||||
        self.assertEqual([], post.info["authors"])
 | 
			
		||||
 | 
			
		||||
    def test_dictfield_dump_document(self):
 | 
			
		||||
        """Ensure a DictField can handle another document's dump."""
 | 
			
		||||
 | 
			
		||||
        class Doc(Document):
 | 
			
		||||
            field = DictField()
 | 
			
		||||
 | 
			
		||||
@@ -106,51 +103,62 @@ class TestDictField(MongoDBTestCase):
 | 
			
		||||
            id = IntField(primary_key=True, default=1)
 | 
			
		||||
            recursive = DictField()
 | 
			
		||||
 | 
			
		||||
            meta = {'allow_inheritance': True}
 | 
			
		||||
            meta = {"allow_inheritance": True}
 | 
			
		||||
 | 
			
		||||
        class ToEmbedChild(ToEmbedParent):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        to_embed_recursive = ToEmbed(id=1).save()
 | 
			
		||||
        to_embed = ToEmbed(
 | 
			
		||||
            id=2, recursive=to_embed_recursive.to_mongo().to_dict()).save()
 | 
			
		||||
            id=2, recursive=to_embed_recursive.to_mongo().to_dict()
 | 
			
		||||
        ).save()
 | 
			
		||||
        doc = Doc(field=to_embed.to_mongo().to_dict())
 | 
			
		||||
        doc.save()
 | 
			
		||||
        self.assertIsInstance(doc.field, dict)
 | 
			
		||||
        self.assertEqual(doc.field, {'_id': 2, 'recursive': {'_id': 1, 'recursive': {}}})
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            doc.field, {"_id": 2, "recursive": {"_id": 1, "recursive": {}}}
 | 
			
		||||
        )
 | 
			
		||||
        # Same thing with a Document with a _cls field
 | 
			
		||||
        to_embed_recursive = ToEmbedChild(id=1).save()
 | 
			
		||||
        to_embed_child = ToEmbedChild(
 | 
			
		||||
            id=2, recursive=to_embed_recursive.to_mongo().to_dict()).save()
 | 
			
		||||
            id=2, recursive=to_embed_recursive.to_mongo().to_dict()
 | 
			
		||||
        ).save()
 | 
			
		||||
        doc = Doc(field=to_embed_child.to_mongo().to_dict())
 | 
			
		||||
        doc.save()
 | 
			
		||||
        self.assertIsInstance(doc.field, dict)
 | 
			
		||||
        expected = {
 | 
			
		||||
            '_id': 2, '_cls': 'ToEmbedParent.ToEmbedChild',
 | 
			
		||||
            'recursive': {'_id': 1, '_cls': 'ToEmbedParent.ToEmbedChild', 'recursive': {}}
 | 
			
		||||
            "_id": 2,
 | 
			
		||||
            "_cls": "ToEmbedParent.ToEmbedChild",
 | 
			
		||||
            "recursive": {
 | 
			
		||||
                "_id": 1,
 | 
			
		||||
                "_cls": "ToEmbedParent.ToEmbedChild",
 | 
			
		||||
                "recursive": {},
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
        self.assertEqual(doc.field, expected)
 | 
			
		||||
 | 
			
		||||
    def test_dictfield_strict(self):
 | 
			
		||||
        """Ensure that dict field handles validation if provided a strict field type."""
 | 
			
		||||
 | 
			
		||||
        class Simple(Document):
 | 
			
		||||
            mapping = DictField(field=IntField())
 | 
			
		||||
 | 
			
		||||
        Simple.drop_collection()
 | 
			
		||||
 | 
			
		||||
        e = Simple()
 | 
			
		||||
        e.mapping['someint'] = 1
 | 
			
		||||
        e.mapping["someint"] = 1
 | 
			
		||||
        e.save()
 | 
			
		||||
 | 
			
		||||
        # try creating an invalid mapping
 | 
			
		||||
        with self.assertRaises(ValidationError):
 | 
			
		||||
            e.mapping['somestring'] = "abc"
 | 
			
		||||
            e.mapping["somestring"] = "abc"
 | 
			
		||||
            e.save()
 | 
			
		||||
 | 
			
		||||
    def test_dictfield_complex(self):
 | 
			
		||||
        """Ensure that the dict field can handle the complex types."""
 | 
			
		||||
 | 
			
		||||
        class SettingBase(EmbeddedDocument):
 | 
			
		||||
            meta = {'allow_inheritance': True}
 | 
			
		||||
            meta = {"allow_inheritance": True}
 | 
			
		||||
 | 
			
		||||
        class StringSetting(SettingBase):
 | 
			
		||||
            value = StringField()
 | 
			
		||||
@@ -164,70 +172,72 @@ class TestDictField(MongoDBTestCase):
 | 
			
		||||
        Simple.drop_collection()
 | 
			
		||||
 | 
			
		||||
        e = Simple()
 | 
			
		||||
        e.mapping['somestring'] = StringSetting(value='foo')
 | 
			
		||||
        e.mapping['someint'] = IntegerSetting(value=42)
 | 
			
		||||
        e.mapping['nested_dict'] = {'number': 1, 'string': 'Hi!',
 | 
			
		||||
                                    'float': 1.001,
 | 
			
		||||
                                    'complex': IntegerSetting(value=42),
 | 
			
		||||
                                    'list': [IntegerSetting(value=42),
 | 
			
		||||
                                             StringSetting(value='foo')]}
 | 
			
		||||
        e.mapping["somestring"] = StringSetting(value="foo")
 | 
			
		||||
        e.mapping["someint"] = IntegerSetting(value=42)
 | 
			
		||||
        e.mapping["nested_dict"] = {
 | 
			
		||||
            "number": 1,
 | 
			
		||||
            "string": "Hi!",
 | 
			
		||||
            "float": 1.001,
 | 
			
		||||
            "complex": IntegerSetting(value=42),
 | 
			
		||||
            "list": [IntegerSetting(value=42), StringSetting(value="foo")],
 | 
			
		||||
        }
 | 
			
		||||
        e.save()
 | 
			
		||||
 | 
			
		||||
        e2 = Simple.objects.get(id=e.id)
 | 
			
		||||
        self.assertIsInstance(e2.mapping['somestring'], StringSetting)
 | 
			
		||||
        self.assertIsInstance(e2.mapping['someint'], IntegerSetting)
 | 
			
		||||
        self.assertIsInstance(e2.mapping["somestring"], StringSetting)
 | 
			
		||||
        self.assertIsInstance(e2.mapping["someint"], IntegerSetting)
 | 
			
		||||
 | 
			
		||||
        # Test querying
 | 
			
		||||
        self.assertEqual(Simple.objects.filter(mapping__someint__value=42).count(), 1)
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            Simple.objects.filter(mapping__someint__value=42).count(), 1)
 | 
			
		||||
            Simple.objects.filter(mapping__nested_dict__number=1).count(), 1
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            Simple.objects.filter(mapping__nested_dict__number=1).count(), 1)
 | 
			
		||||
            Simple.objects.filter(mapping__nested_dict__complex__value=42).count(), 1
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            Simple.objects.filter(mapping__nested_dict__complex__value=42).count(), 1)
 | 
			
		||||
            Simple.objects.filter(mapping__nested_dict__list__0__value=42).count(), 1
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            Simple.objects.filter(mapping__nested_dict__list__0__value=42).count(), 1)
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            Simple.objects.filter(mapping__nested_dict__list__1__value='foo').count(), 1)
 | 
			
		||||
            Simple.objects.filter(mapping__nested_dict__list__1__value="foo").count(), 1
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # Confirm can update
 | 
			
		||||
        Simple.objects().update(set__mapping={"someint": IntegerSetting(value=10)})
 | 
			
		||||
        Simple.objects().update(
 | 
			
		||||
            set__mapping={"someint": IntegerSetting(value=10)})
 | 
			
		||||
        Simple.objects().update(
 | 
			
		||||
            set__mapping__nested_dict__list__1=StringSetting(value='Boo'))
 | 
			
		||||
            set__mapping__nested_dict__list__1=StringSetting(value="Boo")
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            Simple.objects.filter(mapping__nested_dict__list__1__value='foo').count(), 0)
 | 
			
		||||
            Simple.objects.filter(mapping__nested_dict__list__1__value="foo").count(), 0
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            Simple.objects.filter(mapping__nested_dict__list__1__value='Boo').count(), 1)
 | 
			
		||||
            Simple.objects.filter(mapping__nested_dict__list__1__value="Boo").count(), 1
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_push_dict(self):
 | 
			
		||||
        class MyModel(Document):
 | 
			
		||||
            events = ListField(DictField())
 | 
			
		||||
 | 
			
		||||
        doc = MyModel(events=[{'a': 1}]).save()
 | 
			
		||||
        doc = MyModel(events=[{"a": 1}]).save()
 | 
			
		||||
        raw_doc = get_as_pymongo(doc)
 | 
			
		||||
        expected_raw_doc = {
 | 
			
		||||
            '_id': doc.id,
 | 
			
		||||
            'events': [{'a': 1}]
 | 
			
		||||
        }
 | 
			
		||||
        expected_raw_doc = {"_id": doc.id, "events": [{"a": 1}]}
 | 
			
		||||
        self.assertEqual(raw_doc, expected_raw_doc)
 | 
			
		||||
 | 
			
		||||
        MyModel.objects(id=doc.id).update(push__events={})
 | 
			
		||||
        raw_doc = get_as_pymongo(doc)
 | 
			
		||||
        expected_raw_doc = {
 | 
			
		||||
            '_id': doc.id,
 | 
			
		||||
            'events': [{'a': 1}, {}]
 | 
			
		||||
        }
 | 
			
		||||
        expected_raw_doc = {"_id": doc.id, "events": [{"a": 1}, {}]}
 | 
			
		||||
        self.assertEqual(raw_doc, expected_raw_doc)
 | 
			
		||||
 | 
			
		||||
    def test_ensure_unique_default_instances(self):
 | 
			
		||||
        """Ensure that every field has it's own unique default instance."""
 | 
			
		||||
 | 
			
		||||
        class D(Document):
 | 
			
		||||
            data = DictField()
 | 
			
		||||
            data2 = DictField(default=lambda: {})
 | 
			
		||||
 | 
			
		||||
        d1 = D()
 | 
			
		||||
        d1.data['foo'] = 'bar'
 | 
			
		||||
        d1.data2['foo'] = 'bar'
 | 
			
		||||
        d1.data["foo"] = "bar"
 | 
			
		||||
        d1.data2["foo"] = "bar"
 | 
			
		||||
        d2 = D()
 | 
			
		||||
        self.assertEqual(d2.data, {})
 | 
			
		||||
        self.assertEqual(d2.data2, {})
 | 
			
		||||
@@ -255,22 +265,25 @@ class TestDictField(MongoDBTestCase):
 | 
			
		||||
        class Embedded(EmbeddedDocument):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
        embed = Embedded(name='garbage')
 | 
			
		||||
        embed = Embedded(name="garbage")
 | 
			
		||||
        doc = DictFieldTest(dictionary=embed)
 | 
			
		||||
        with self.assertRaises(ValidationError) as ctx_err:
 | 
			
		||||
            doc.validate()
 | 
			
		||||
        self.assertIn("'dictionary'", str(ctx_err.exception))
 | 
			
		||||
        self.assertIn('Only dictionaries may be used in a DictField', str(ctx_err.exception))
 | 
			
		||||
        self.assertIn(
 | 
			
		||||
            "Only dictionaries may be used in a DictField", str(ctx_err.exception)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_atomic_update_dict_field(self):
 | 
			
		||||
        """Ensure that the entire DictField can be atomically updated."""
 | 
			
		||||
 | 
			
		||||
        class Simple(Document):
 | 
			
		||||
            mapping = DictField(field=ListField(IntField(required=True)))
 | 
			
		||||
 | 
			
		||||
        Simple.drop_collection()
 | 
			
		||||
 | 
			
		||||
        e = Simple()
 | 
			
		||||
        e.mapping['someints'] = [1, 2]
 | 
			
		||||
        e.mapping["someints"] = [1, 2]
 | 
			
		||||
        e.save()
 | 
			
		||||
        e.update(set__mapping={"ints": [3, 4]})
 | 
			
		||||
        e.reload()
 | 
			
		||||
@@ -279,7 +292,7 @@ class TestDictField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        # try creating an invalid mapping
 | 
			
		||||
        with self.assertRaises(ValueError):
 | 
			
		||||
            e.update(set__mapping={"somestrings": ["foo", "bar", ]})
 | 
			
		||||
            e.update(set__mapping={"somestrings": ["foo", "bar"]})
 | 
			
		||||
 | 
			
		||||
    def test_dictfield_with_referencefield_complex_nesting_cases(self):
 | 
			
		||||
        """Ensure complex nesting inside DictField handles dereferencing of ReferenceField(dbref=True | False)"""
 | 
			
		||||
@@ -296,29 +309,33 @@ class TestDictField(MongoDBTestCase):
 | 
			
		||||
            mapping5 = DictField(DictField(field=ReferenceField(Doc, dbref=False)))
 | 
			
		||||
            mapping6 = DictField(ListField(DictField(ReferenceField(Doc, dbref=True))))
 | 
			
		||||
            mapping7 = DictField(ListField(DictField(ReferenceField(Doc, dbref=False))))
 | 
			
		||||
            mapping8 = DictField(ListField(DictField(ListField(ReferenceField(Doc, dbref=True)))))
 | 
			
		||||
            mapping9 = DictField(ListField(DictField(ListField(ReferenceField(Doc, dbref=False)))))
 | 
			
		||||
            mapping8 = DictField(
 | 
			
		||||
                ListField(DictField(ListField(ReferenceField(Doc, dbref=True))))
 | 
			
		||||
            )
 | 
			
		||||
            mapping9 = DictField(
 | 
			
		||||
                ListField(DictField(ListField(ReferenceField(Doc, dbref=False))))
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        Doc.drop_collection()
 | 
			
		||||
        Simple.drop_collection()
 | 
			
		||||
 | 
			
		||||
        d = Doc(s='aa').save()
 | 
			
		||||
        d = Doc(s="aa").save()
 | 
			
		||||
        e = Simple()
 | 
			
		||||
        e.mapping0['someint'] = e.mapping1['someint'] = d
 | 
			
		||||
        e.mapping2['someint'] = e.mapping3['someint'] = [d]
 | 
			
		||||
        e.mapping4['someint'] = e.mapping5['someint'] = {'d': d}
 | 
			
		||||
        e.mapping6['someint'] = e.mapping7['someint'] = [{'d': d}]
 | 
			
		||||
        e.mapping8['someint'] = e.mapping9['someint'] = [{'d': [d]}]
 | 
			
		||||
        e.mapping0["someint"] = e.mapping1["someint"] = d
 | 
			
		||||
        e.mapping2["someint"] = e.mapping3["someint"] = [d]
 | 
			
		||||
        e.mapping4["someint"] = e.mapping5["someint"] = {"d": d}
 | 
			
		||||
        e.mapping6["someint"] = e.mapping7["someint"] = [{"d": d}]
 | 
			
		||||
        e.mapping8["someint"] = e.mapping9["someint"] = [{"d": [d]}]
 | 
			
		||||
        e.save()
 | 
			
		||||
 | 
			
		||||
        s = Simple.objects.first()
 | 
			
		||||
        self.assertIsInstance(s.mapping0['someint'], Doc)
 | 
			
		||||
        self.assertIsInstance(s.mapping1['someint'], Doc)
 | 
			
		||||
        self.assertIsInstance(s.mapping2['someint'][0], Doc)
 | 
			
		||||
        self.assertIsInstance(s.mapping3['someint'][0], Doc)
 | 
			
		||||
        self.assertIsInstance(s.mapping4['someint']['d'], Doc)
 | 
			
		||||
        self.assertIsInstance(s.mapping5['someint']['d'], Doc)
 | 
			
		||||
        self.assertIsInstance(s.mapping6['someint'][0]['d'], Doc)
 | 
			
		||||
        self.assertIsInstance(s.mapping7['someint'][0]['d'], Doc)
 | 
			
		||||
        self.assertIsInstance(s.mapping8['someint'][0]['d'][0], Doc)
 | 
			
		||||
        self.assertIsInstance(s.mapping9['someint'][0]['d'][0], Doc)
 | 
			
		||||
        self.assertIsInstance(s.mapping0["someint"], Doc)
 | 
			
		||||
        self.assertIsInstance(s.mapping1["someint"], Doc)
 | 
			
		||||
        self.assertIsInstance(s.mapping2["someint"][0], Doc)
 | 
			
		||||
        self.assertIsInstance(s.mapping3["someint"][0], Doc)
 | 
			
		||||
        self.assertIsInstance(s.mapping4["someint"]["d"], Doc)
 | 
			
		||||
        self.assertIsInstance(s.mapping5["someint"]["d"], Doc)
 | 
			
		||||
        self.assertIsInstance(s.mapping6["someint"][0]["d"], Doc)
 | 
			
		||||
        self.assertIsInstance(s.mapping7["someint"][0]["d"], Doc)
 | 
			
		||||
        self.assertIsInstance(s.mapping8["someint"][0]["d"][0], Doc)
 | 
			
		||||
        self.assertIsInstance(s.mapping9["someint"][0]["d"][0], Doc)
 | 
			
		||||
 
 | 
			
		||||
@@ -12,28 +12,29 @@ class TestEmailField(MongoDBTestCase):
 | 
			
		||||
        class User(Document):
 | 
			
		||||
            email = EmailField()
 | 
			
		||||
 | 
			
		||||
        user = User(email='ross@example.com')
 | 
			
		||||
        user = User(email="ross@example.com")
 | 
			
		||||
        user.validate()
 | 
			
		||||
 | 
			
		||||
        user = User(email='ross@example.co.uk')
 | 
			
		||||
        user = User(email="ross@example.co.uk")
 | 
			
		||||
        user.validate()
 | 
			
		||||
 | 
			
		||||
        user = User(email=('Kofq@rhom0e4klgauOhpbpNdogawnyIKvQS0wk2mjqrgGQ5S'
 | 
			
		||||
                           'aJIazqqWkm7.net'))
 | 
			
		||||
        user = User(
 | 
			
		||||
            email=("Kofq@rhom0e4klgauOhpbpNdogawnyIKvQS0wk2mjqrgGQ5SaJIazqqWkm7.net")
 | 
			
		||||
        )
 | 
			
		||||
        user.validate()
 | 
			
		||||
 | 
			
		||||
        user = User(email='new-tld@example.technology')
 | 
			
		||||
        user = User(email="new-tld@example.technology")
 | 
			
		||||
        user.validate()
 | 
			
		||||
 | 
			
		||||
        user = User(email='ross@example.com.')
 | 
			
		||||
        user = User(email="ross@example.com.")
 | 
			
		||||
        self.assertRaises(ValidationError, user.validate)
 | 
			
		||||
 | 
			
		||||
        # unicode domain
 | 
			
		||||
        user = User(email=u'user@пример.рф')
 | 
			
		||||
        user = User(email=u"user@пример.рф")
 | 
			
		||||
        user.validate()
 | 
			
		||||
 | 
			
		||||
        # invalid unicode domain
 | 
			
		||||
        user = User(email=u'user@пример')
 | 
			
		||||
        user = User(email=u"user@пример")
 | 
			
		||||
        self.assertRaises(ValidationError, user.validate)
 | 
			
		||||
 | 
			
		||||
        # invalid data type
 | 
			
		||||
@@ -44,20 +45,20 @@ class TestEmailField(MongoDBTestCase):
 | 
			
		||||
        # Don't run this test on pypy3, which doesn't support unicode regex:
 | 
			
		||||
        # https://bitbucket.org/pypy/pypy/issues/1821/regular-expression-doesnt-find-unicode
 | 
			
		||||
        if sys.version_info[:2] == (3, 2):
 | 
			
		||||
            raise SkipTest('unicode email addresses are not supported on PyPy 3')
 | 
			
		||||
            raise SkipTest("unicode email addresses are not supported on PyPy 3")
 | 
			
		||||
 | 
			
		||||
        class User(Document):
 | 
			
		||||
            email = EmailField()
 | 
			
		||||
 | 
			
		||||
        # unicode user shouldn't validate by default...
 | 
			
		||||
        user = User(email=u'Dörte@Sörensen.example.com')
 | 
			
		||||
        user = User(email=u"Dörte@Sörensen.example.com")
 | 
			
		||||
        self.assertRaises(ValidationError, user.validate)
 | 
			
		||||
 | 
			
		||||
        # ...but it should be fine with allow_utf8_user set to True
 | 
			
		||||
        class User(Document):
 | 
			
		||||
            email = EmailField(allow_utf8_user=True)
 | 
			
		||||
 | 
			
		||||
        user = User(email=u'Dörte@Sörensen.example.com')
 | 
			
		||||
        user = User(email=u"Dörte@Sörensen.example.com")
 | 
			
		||||
        user.validate()
 | 
			
		||||
 | 
			
		||||
    def test_email_field_domain_whitelist(self):
 | 
			
		||||
@@ -65,22 +66,22 @@ class TestEmailField(MongoDBTestCase):
 | 
			
		||||
            email = EmailField()
 | 
			
		||||
 | 
			
		||||
        # localhost domain shouldn't validate by default...
 | 
			
		||||
        user = User(email='me@localhost')
 | 
			
		||||
        user = User(email="me@localhost")
 | 
			
		||||
        self.assertRaises(ValidationError, user.validate)
 | 
			
		||||
 | 
			
		||||
        # ...but it should be fine if it's whitelisted
 | 
			
		||||
        class User(Document):
 | 
			
		||||
            email = EmailField(domain_whitelist=['localhost'])
 | 
			
		||||
            email = EmailField(domain_whitelist=["localhost"])
 | 
			
		||||
 | 
			
		||||
        user = User(email='me@localhost')
 | 
			
		||||
        user = User(email="me@localhost")
 | 
			
		||||
        user.validate()
 | 
			
		||||
 | 
			
		||||
    def test_email_domain_validation_fails_if_invalid_idn(self):
 | 
			
		||||
        class User(Document):
 | 
			
		||||
            email = EmailField()
 | 
			
		||||
 | 
			
		||||
        invalid_idn = '.google.com'
 | 
			
		||||
        user = User(email='me@%s' % invalid_idn)
 | 
			
		||||
        invalid_idn = ".google.com"
 | 
			
		||||
        user = User(email="me@%s" % invalid_idn)
 | 
			
		||||
        with self.assertRaises(ValidationError) as ctx_err:
 | 
			
		||||
            user.validate()
 | 
			
		||||
        self.assertIn("domain failed IDN encoding", str(ctx_err.exception))
 | 
			
		||||
@@ -89,9 +90,9 @@ class TestEmailField(MongoDBTestCase):
 | 
			
		||||
        class User(Document):
 | 
			
		||||
            email = EmailField()
 | 
			
		||||
 | 
			
		||||
        valid_ipv4 = 'email@[127.0.0.1]'
 | 
			
		||||
        valid_ipv6 = 'email@[2001:dB8::1]'
 | 
			
		||||
        invalid_ip = 'email@[324.0.0.1]'
 | 
			
		||||
        valid_ipv4 = "email@[127.0.0.1]"
 | 
			
		||||
        valid_ipv6 = "email@[2001:dB8::1]"
 | 
			
		||||
        invalid_ip = "email@[324.0.0.1]"
 | 
			
		||||
 | 
			
		||||
        # IP address as a domain shouldn't validate by default...
 | 
			
		||||
        user = User(email=valid_ipv4)
 | 
			
		||||
@@ -119,12 +120,12 @@ class TestEmailField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_email_field_honors_regex(self):
 | 
			
		||||
        class User(Document):
 | 
			
		||||
            email = EmailField(regex=r'\w+@example.com')
 | 
			
		||||
            email = EmailField(regex=r"\w+@example.com")
 | 
			
		||||
 | 
			
		||||
        # Fails regex validation
 | 
			
		||||
        user = User(email='me@foo.com')
 | 
			
		||||
        user = User(email="me@foo.com")
 | 
			
		||||
        self.assertRaises(ValidationError, user.validate)
 | 
			
		||||
 | 
			
		||||
        # Passes regex validation
 | 
			
		||||
        user = User(email='me@example.com')
 | 
			
		||||
        user = User(email="me@example.com")
 | 
			
		||||
        self.assertIsNone(user.validate())
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,18 @@
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
from mongoengine import Document, StringField, ValidationError, EmbeddedDocument, EmbeddedDocumentField, \
 | 
			
		||||
    InvalidQueryError, LookUpError, IntField, GenericEmbeddedDocumentField, ListField, EmbeddedDocumentListField, \
 | 
			
		||||
    ReferenceField
 | 
			
		||||
from mongoengine import (
 | 
			
		||||
    Document,
 | 
			
		||||
    StringField,
 | 
			
		||||
    ValidationError,
 | 
			
		||||
    EmbeddedDocument,
 | 
			
		||||
    EmbeddedDocumentField,
 | 
			
		||||
    InvalidQueryError,
 | 
			
		||||
    LookUpError,
 | 
			
		||||
    IntField,
 | 
			
		||||
    GenericEmbeddedDocumentField,
 | 
			
		||||
    ListField,
 | 
			
		||||
    EmbeddedDocumentListField,
 | 
			
		||||
    ReferenceField,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
from tests.utils import MongoDBTestCase
 | 
			
		||||
 | 
			
		||||
@@ -14,22 +25,24 @@ class TestEmbeddedDocumentField(MongoDBTestCase):
 | 
			
		||||
        field = EmbeddedDocumentField(MyDoc)
 | 
			
		||||
        self.assertEqual(field.document_type_obj, MyDoc)
 | 
			
		||||
 | 
			
		||||
        field2 = EmbeddedDocumentField('MyDoc')
 | 
			
		||||
        self.assertEqual(field2.document_type_obj, 'MyDoc')
 | 
			
		||||
        field2 = EmbeddedDocumentField("MyDoc")
 | 
			
		||||
        self.assertEqual(field2.document_type_obj, "MyDoc")
 | 
			
		||||
 | 
			
		||||
    def test___init___throw_error_if_document_type_is_not_EmbeddedDocument(self):
 | 
			
		||||
        with self.assertRaises(ValidationError):
 | 
			
		||||
            EmbeddedDocumentField(dict)
 | 
			
		||||
 | 
			
		||||
    def test_document_type_throw_error_if_not_EmbeddedDocument_subclass(self):
 | 
			
		||||
 | 
			
		||||
        class MyDoc(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
        emb = EmbeddedDocumentField('MyDoc')
 | 
			
		||||
        emb = EmbeddedDocumentField("MyDoc")
 | 
			
		||||
        with self.assertRaises(ValidationError) as ctx:
 | 
			
		||||
            emb.document_type
 | 
			
		||||
        self.assertIn('Invalid embedded document class provided to an EmbeddedDocumentField', str(ctx.exception))
 | 
			
		||||
        self.assertIn(
 | 
			
		||||
            "Invalid embedded document class provided to an EmbeddedDocumentField",
 | 
			
		||||
            str(ctx.exception),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_embedded_document_field_only_allow_subclasses_of_embedded_document(self):
 | 
			
		||||
        # Relates to #1661
 | 
			
		||||
@@ -37,12 +50,14 @@ class TestEmbeddedDocumentField(MongoDBTestCase):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
        with self.assertRaises(ValidationError):
 | 
			
		||||
 | 
			
		||||
            class MyFailingDoc(Document):
 | 
			
		||||
                emb = EmbeddedDocumentField(MyDoc)
 | 
			
		||||
 | 
			
		||||
        with self.assertRaises(ValidationError):
 | 
			
		||||
 | 
			
		||||
            class MyFailingdoc2(Document):
 | 
			
		||||
                emb = EmbeddedDocumentField('MyDoc')
 | 
			
		||||
                emb = EmbeddedDocumentField("MyDoc")
 | 
			
		||||
 | 
			
		||||
    def test_query_embedded_document_attribute(self):
 | 
			
		||||
        class AdminSettings(EmbeddedDocument):
 | 
			
		||||
@@ -55,34 +70,31 @@ class TestEmbeddedDocumentField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        Person.drop_collection()
 | 
			
		||||
 | 
			
		||||
        p = Person(
 | 
			
		||||
            settings=AdminSettings(foo1='bar1', foo2='bar2'),
 | 
			
		||||
            name='John',
 | 
			
		||||
        ).save()
 | 
			
		||||
        p = Person(settings=AdminSettings(foo1="bar1", foo2="bar2"), name="John").save()
 | 
			
		||||
 | 
			
		||||
        # Test non exiting attribute
 | 
			
		||||
        with self.assertRaises(InvalidQueryError) as ctx_err:
 | 
			
		||||
            Person.objects(settings__notexist='bar').first()
 | 
			
		||||
            Person.objects(settings__notexist="bar").first()
 | 
			
		||||
        self.assertEqual(unicode(ctx_err.exception), u'Cannot resolve field "notexist"')
 | 
			
		||||
 | 
			
		||||
        with self.assertRaises(LookUpError):
 | 
			
		||||
            Person.objects.only('settings.notexist')
 | 
			
		||||
            Person.objects.only("settings.notexist")
 | 
			
		||||
 | 
			
		||||
        # Test existing attribute
 | 
			
		||||
        self.assertEqual(Person.objects(settings__foo1='bar1').first().id, p.id)
 | 
			
		||||
        only_p = Person.objects.only('settings.foo1').first()
 | 
			
		||||
        self.assertEqual(Person.objects(settings__foo1="bar1").first().id, p.id)
 | 
			
		||||
        only_p = Person.objects.only("settings.foo1").first()
 | 
			
		||||
        self.assertEqual(only_p.settings.foo1, p.settings.foo1)
 | 
			
		||||
        self.assertIsNone(only_p.settings.foo2)
 | 
			
		||||
        self.assertIsNone(only_p.name)
 | 
			
		||||
 | 
			
		||||
        exclude_p = Person.objects.exclude('settings.foo1').first()
 | 
			
		||||
        exclude_p = Person.objects.exclude("settings.foo1").first()
 | 
			
		||||
        self.assertIsNone(exclude_p.settings.foo1)
 | 
			
		||||
        self.assertEqual(exclude_p.settings.foo2, p.settings.foo2)
 | 
			
		||||
        self.assertEqual(exclude_p.name, p.name)
 | 
			
		||||
 | 
			
		||||
    def test_query_embedded_document_attribute_with_inheritance(self):
 | 
			
		||||
        class BaseSettings(EmbeddedDocument):
 | 
			
		||||
            meta = {'allow_inheritance': True}
 | 
			
		||||
            meta = {"allow_inheritance": True}
 | 
			
		||||
            base_foo = StringField()
 | 
			
		||||
 | 
			
		||||
        class AdminSettings(BaseSettings):
 | 
			
		||||
@@ -93,26 +105,26 @@ class TestEmbeddedDocumentField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        Person.drop_collection()
 | 
			
		||||
 | 
			
		||||
        p = Person(settings=AdminSettings(base_foo='basefoo', sub_foo='subfoo'))
 | 
			
		||||
        p = Person(settings=AdminSettings(base_foo="basefoo", sub_foo="subfoo"))
 | 
			
		||||
        p.save()
 | 
			
		||||
 | 
			
		||||
        # Test non exiting attribute
 | 
			
		||||
        with self.assertRaises(InvalidQueryError) as ctx_err:
 | 
			
		||||
            self.assertEqual(Person.objects(settings__notexist='bar').first().id, p.id)
 | 
			
		||||
            self.assertEqual(Person.objects(settings__notexist="bar").first().id, p.id)
 | 
			
		||||
        self.assertEqual(unicode(ctx_err.exception), u'Cannot resolve field "notexist"')
 | 
			
		||||
 | 
			
		||||
        # Test existing attribute
 | 
			
		||||
        self.assertEqual(Person.objects(settings__base_foo='basefoo').first().id, p.id)
 | 
			
		||||
        self.assertEqual(Person.objects(settings__sub_foo='subfoo').first().id, p.id)
 | 
			
		||||
        self.assertEqual(Person.objects(settings__base_foo="basefoo").first().id, p.id)
 | 
			
		||||
        self.assertEqual(Person.objects(settings__sub_foo="subfoo").first().id, p.id)
 | 
			
		||||
 | 
			
		||||
        only_p = Person.objects.only('settings.base_foo', 'settings._cls').first()
 | 
			
		||||
        self.assertEqual(only_p.settings.base_foo, 'basefoo')
 | 
			
		||||
        only_p = Person.objects.only("settings.base_foo", "settings._cls").first()
 | 
			
		||||
        self.assertEqual(only_p.settings.base_foo, "basefoo")
 | 
			
		||||
        self.assertIsNone(only_p.settings.sub_foo)
 | 
			
		||||
 | 
			
		||||
    def test_query_list_embedded_document_with_inheritance(self):
 | 
			
		||||
        class Post(EmbeddedDocument):
 | 
			
		||||
            title = StringField(max_length=120, required=True)
 | 
			
		||||
            meta = {'allow_inheritance': True}
 | 
			
		||||
            meta = {"allow_inheritance": True}
 | 
			
		||||
 | 
			
		||||
        class TextPost(Post):
 | 
			
		||||
            content = StringField()
 | 
			
		||||
@@ -123,8 +135,8 @@ class TestEmbeddedDocumentField(MongoDBTestCase):
 | 
			
		||||
        class Record(Document):
 | 
			
		||||
            posts = ListField(EmbeddedDocumentField(Post))
 | 
			
		||||
 | 
			
		||||
        record_movie = Record(posts=[MoviePost(author='John', title='foo')]).save()
 | 
			
		||||
        record_text = Record(posts=[TextPost(content='a', title='foo')]).save()
 | 
			
		||||
        record_movie = Record(posts=[MoviePost(author="John", title="foo")]).save()
 | 
			
		||||
        record_text = Record(posts=[TextPost(content="a", title="foo")]).save()
 | 
			
		||||
 | 
			
		||||
        records = list(Record.objects(posts__author=record_movie.posts[0].author))
 | 
			
		||||
        self.assertEqual(len(records), 1)
 | 
			
		||||
@@ -134,11 +146,10 @@ class TestEmbeddedDocumentField(MongoDBTestCase):
 | 
			
		||||
        self.assertEqual(len(records), 1)
 | 
			
		||||
        self.assertEqual(records[0].id, record_text.id)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(Record.objects(posts__title='foo').count(), 2)
 | 
			
		||||
        self.assertEqual(Record.objects(posts__title="foo").count(), 2)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestGenericEmbeddedDocumentField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_generic_embedded_document(self):
 | 
			
		||||
        class Car(EmbeddedDocument):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
@@ -153,8 +164,8 @@ class TestGenericEmbeddedDocumentField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        Person.drop_collection()
 | 
			
		||||
 | 
			
		||||
        person = Person(name='Test User')
 | 
			
		||||
        person.like = Car(name='Fiat')
 | 
			
		||||
        person = Person(name="Test User")
 | 
			
		||||
        person.like = Car(name="Fiat")
 | 
			
		||||
        person.save()
 | 
			
		||||
 | 
			
		||||
        person = Person.objects.first()
 | 
			
		||||
@@ -168,6 +179,7 @@ class TestGenericEmbeddedDocumentField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_generic_embedded_document_choices(self):
 | 
			
		||||
        """Ensure you can limit GenericEmbeddedDocument choices."""
 | 
			
		||||
 | 
			
		||||
        class Car(EmbeddedDocument):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
@@ -181,8 +193,8 @@ class TestGenericEmbeddedDocumentField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        Person.drop_collection()
 | 
			
		||||
 | 
			
		||||
        person = Person(name='Test User')
 | 
			
		||||
        person.like = Car(name='Fiat')
 | 
			
		||||
        person = Person(name="Test User")
 | 
			
		||||
        person.like = Car(name="Fiat")
 | 
			
		||||
        self.assertRaises(ValidationError, person.validate)
 | 
			
		||||
 | 
			
		||||
        person.like = Dish(food="arroz", number=15)
 | 
			
		||||
@@ -195,6 +207,7 @@ class TestGenericEmbeddedDocumentField(MongoDBTestCase):
 | 
			
		||||
        """Ensure you can limit GenericEmbeddedDocument choices inside
 | 
			
		||||
        a list field.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Car(EmbeddedDocument):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
@@ -208,8 +221,8 @@ class TestGenericEmbeddedDocumentField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        Person.drop_collection()
 | 
			
		||||
 | 
			
		||||
        person = Person(name='Test User')
 | 
			
		||||
        person.likes = [Car(name='Fiat')]
 | 
			
		||||
        person = Person(name="Test User")
 | 
			
		||||
        person.likes = [Car(name="Fiat")]
 | 
			
		||||
        self.assertRaises(ValidationError, person.validate)
 | 
			
		||||
 | 
			
		||||
        person.likes = [Dish(food="arroz", number=15)]
 | 
			
		||||
@@ -222,25 +235,23 @@ class TestGenericEmbeddedDocumentField(MongoDBTestCase):
 | 
			
		||||
        """
 | 
			
		||||
        Ensure fields with document choices validate given a valid choice.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class UserComments(EmbeddedDocument):
 | 
			
		||||
            author = StringField()
 | 
			
		||||
            message = StringField()
 | 
			
		||||
 | 
			
		||||
        class BlogPost(Document):
 | 
			
		||||
            comments = ListField(
 | 
			
		||||
                GenericEmbeddedDocumentField(choices=(UserComments,))
 | 
			
		||||
            )
 | 
			
		||||
            comments = ListField(GenericEmbeddedDocumentField(choices=(UserComments,)))
 | 
			
		||||
 | 
			
		||||
        # Ensure Validation Passes
 | 
			
		||||
        BlogPost(comments=[
 | 
			
		||||
            UserComments(author='user2', message='message2'),
 | 
			
		||||
        ]).save()
 | 
			
		||||
        BlogPost(comments=[UserComments(author="user2", message="message2")]).save()
 | 
			
		||||
 | 
			
		||||
    def test_choices_validation_documents_invalid(self):
 | 
			
		||||
        """
 | 
			
		||||
        Ensure fields with document choices validate given an invalid choice.
 | 
			
		||||
        This should throw a ValidationError exception.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class UserComments(EmbeddedDocument):
 | 
			
		||||
            author = StringField()
 | 
			
		||||
            message = StringField()
 | 
			
		||||
@@ -250,31 +261,28 @@ class TestGenericEmbeddedDocumentField(MongoDBTestCase):
 | 
			
		||||
            message = StringField()
 | 
			
		||||
 | 
			
		||||
        class BlogPost(Document):
 | 
			
		||||
            comments = ListField(
 | 
			
		||||
                GenericEmbeddedDocumentField(choices=(UserComments,))
 | 
			
		||||
            )
 | 
			
		||||
            comments = ListField(GenericEmbeddedDocumentField(choices=(UserComments,)))
 | 
			
		||||
 | 
			
		||||
        # Single Entry Failure
 | 
			
		||||
        post = BlogPost(comments=[
 | 
			
		||||
            ModeratorComments(author='mod1', message='message1'),
 | 
			
		||||
        ])
 | 
			
		||||
        post = BlogPost(comments=[ModeratorComments(author="mod1", message="message1")])
 | 
			
		||||
        self.assertRaises(ValidationError, post.save)
 | 
			
		||||
 | 
			
		||||
        # Mixed Entry Failure
 | 
			
		||||
        post = BlogPost(comments=[
 | 
			
		||||
            ModeratorComments(author='mod1', message='message1'),
 | 
			
		||||
            UserComments(author='user2', message='message2'),
 | 
			
		||||
        ])
 | 
			
		||||
        post = BlogPost(
 | 
			
		||||
            comments=[
 | 
			
		||||
                ModeratorComments(author="mod1", message="message1"),
 | 
			
		||||
                UserComments(author="user2", message="message2"),
 | 
			
		||||
            ]
 | 
			
		||||
        )
 | 
			
		||||
        self.assertRaises(ValidationError, post.save)
 | 
			
		||||
 | 
			
		||||
    def test_choices_validation_documents_inheritance(self):
 | 
			
		||||
        """
 | 
			
		||||
        Ensure fields with document choices validate given subclass of choice.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Comments(EmbeddedDocument):
 | 
			
		||||
            meta = {
 | 
			
		||||
                'abstract': True
 | 
			
		||||
            }
 | 
			
		||||
            meta = {"abstract": True}
 | 
			
		||||
            author = StringField()
 | 
			
		||||
            message = StringField()
 | 
			
		||||
 | 
			
		||||
@@ -282,14 +290,10 @@ class TestGenericEmbeddedDocumentField(MongoDBTestCase):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        class BlogPost(Document):
 | 
			
		||||
            comments = ListField(
 | 
			
		||||
                GenericEmbeddedDocumentField(choices=(Comments,))
 | 
			
		||||
            )
 | 
			
		||||
            comments = ListField(GenericEmbeddedDocumentField(choices=(Comments,)))
 | 
			
		||||
 | 
			
		||||
        # Save Valid EmbeddedDocument Type
 | 
			
		||||
        BlogPost(comments=[
 | 
			
		||||
            UserComments(author='user2', message='message2'),
 | 
			
		||||
        ]).save()
 | 
			
		||||
        BlogPost(comments=[UserComments(author="user2", message="message2")]).save()
 | 
			
		||||
 | 
			
		||||
    def test_query_generic_embedded_document_attribute(self):
 | 
			
		||||
        class AdminSettings(EmbeddedDocument):
 | 
			
		||||
@@ -299,28 +303,30 @@ class TestGenericEmbeddedDocumentField(MongoDBTestCase):
 | 
			
		||||
            foo2 = StringField()
 | 
			
		||||
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            settings = GenericEmbeddedDocumentField(choices=(AdminSettings, NonAdminSettings))
 | 
			
		||||
            settings = GenericEmbeddedDocumentField(
 | 
			
		||||
                choices=(AdminSettings, NonAdminSettings)
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        Person.drop_collection()
 | 
			
		||||
 | 
			
		||||
        p1 = Person(settings=AdminSettings(foo1='bar1')).save()
 | 
			
		||||
        p2 = Person(settings=NonAdminSettings(foo2='bar2')).save()
 | 
			
		||||
        p1 = Person(settings=AdminSettings(foo1="bar1")).save()
 | 
			
		||||
        p2 = Person(settings=NonAdminSettings(foo2="bar2")).save()
 | 
			
		||||
 | 
			
		||||
        # Test non exiting attribute
 | 
			
		||||
        with self.assertRaises(InvalidQueryError) as ctx_err:
 | 
			
		||||
            Person.objects(settings__notexist='bar').first()
 | 
			
		||||
            Person.objects(settings__notexist="bar").first()
 | 
			
		||||
        self.assertEqual(unicode(ctx_err.exception), u'Cannot resolve field "notexist"')
 | 
			
		||||
 | 
			
		||||
        with self.assertRaises(LookUpError):
 | 
			
		||||
            Person.objects.only('settings.notexist')
 | 
			
		||||
            Person.objects.only("settings.notexist")
 | 
			
		||||
 | 
			
		||||
        # Test existing attribute
 | 
			
		||||
        self.assertEqual(Person.objects(settings__foo1='bar1').first().id, p1.id)
 | 
			
		||||
        self.assertEqual(Person.objects(settings__foo2='bar2').first().id, p2.id)
 | 
			
		||||
        self.assertEqual(Person.objects(settings__foo1="bar1").first().id, p1.id)
 | 
			
		||||
        self.assertEqual(Person.objects(settings__foo2="bar2").first().id, p2.id)
 | 
			
		||||
 | 
			
		||||
    def test_query_generic_embedded_document_attribute_with_inheritance(self):
 | 
			
		||||
        class BaseSettings(EmbeddedDocument):
 | 
			
		||||
            meta = {'allow_inheritance': True}
 | 
			
		||||
            meta = {"allow_inheritance": True}
 | 
			
		||||
            base_foo = StringField()
 | 
			
		||||
 | 
			
		||||
        class AdminSettings(BaseSettings):
 | 
			
		||||
@@ -331,14 +337,14 @@ class TestGenericEmbeddedDocumentField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        Person.drop_collection()
 | 
			
		||||
 | 
			
		||||
        p = Person(settings=AdminSettings(base_foo='basefoo', sub_foo='subfoo'))
 | 
			
		||||
        p = Person(settings=AdminSettings(base_foo="basefoo", sub_foo="subfoo"))
 | 
			
		||||
        p.save()
 | 
			
		||||
 | 
			
		||||
        # Test non exiting attribute
 | 
			
		||||
        with self.assertRaises(InvalidQueryError) as ctx_err:
 | 
			
		||||
            self.assertEqual(Person.objects(settings__notexist='bar').first().id, p.id)
 | 
			
		||||
            self.assertEqual(Person.objects(settings__notexist="bar").first().id, p.id)
 | 
			
		||||
        self.assertEqual(unicode(ctx_err.exception), u'Cannot resolve field "notexist"')
 | 
			
		||||
 | 
			
		||||
        # Test existing attribute
 | 
			
		||||
        self.assertEqual(Person.objects(settings__base_foo='basefoo').first().id, p.id)
 | 
			
		||||
        self.assertEqual(Person.objects(settings__sub_foo='subfoo').first().id, p.id)
 | 
			
		||||
        self.assertEqual(Person.objects(settings__base_foo="basefoo").first().id, p.id)
 | 
			
		||||
        self.assertEqual(Person.objects(settings__sub_foo="subfoo").first().id, p.id)
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,6 @@ from tests.utils import MongoDBTestCase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestFloatField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_float_ne_operator(self):
 | 
			
		||||
        class TestDocument(Document):
 | 
			
		||||
            float_fld = FloatField()
 | 
			
		||||
@@ -23,6 +22,7 @@ class TestFloatField(MongoDBTestCase):
 | 
			
		||||
    def test_validation(self):
 | 
			
		||||
        """Ensure that invalid values cannot be assigned to float fields.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            height = FloatField(min_value=0.1, max_value=3.5)
 | 
			
		||||
 | 
			
		||||
@@ -33,7 +33,7 @@ class TestFloatField(MongoDBTestCase):
 | 
			
		||||
        person.height = 1.89
 | 
			
		||||
        person.validate()
 | 
			
		||||
 | 
			
		||||
        person.height = '2.0'
 | 
			
		||||
        person.height = "2.0"
 | 
			
		||||
        self.assertRaises(ValidationError, person.validate)
 | 
			
		||||
 | 
			
		||||
        person.height = 0.01
 | 
			
		||||
@@ -42,7 +42,7 @@ class TestFloatField(MongoDBTestCase):
 | 
			
		||||
        person.height = 4.0
 | 
			
		||||
        self.assertRaises(ValidationError, person.validate)
 | 
			
		||||
 | 
			
		||||
        person_2 = Person(height='something invalid')
 | 
			
		||||
        person_2 = Person(height="something invalid")
 | 
			
		||||
        self.assertRaises(ValidationError, person_2.validate)
 | 
			
		||||
 | 
			
		||||
        big_person = BigPerson()
 | 
			
		||||
 
 | 
			
		||||
@@ -5,10 +5,10 @@ from tests.utils import MongoDBTestCase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestIntField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_int_validation(self):
 | 
			
		||||
        """Ensure that invalid values cannot be assigned to int fields.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            age = IntField(min_value=0, max_value=110)
 | 
			
		||||
 | 
			
		||||
@@ -26,7 +26,7 @@ class TestIntField(MongoDBTestCase):
 | 
			
		||||
        self.assertRaises(ValidationError, person.validate)
 | 
			
		||||
        person.age = 120
 | 
			
		||||
        self.assertRaises(ValidationError, person.validate)
 | 
			
		||||
        person.age = 'ten'
 | 
			
		||||
        person.age = "ten"
 | 
			
		||||
        self.assertRaises(ValidationError, person.validate)
 | 
			
		||||
 | 
			
		||||
    def test_ne_operator(self):
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@ class TestLazyReferenceField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        animal = Animal()
 | 
			
		||||
        oc = Ocurrence(animal=animal)
 | 
			
		||||
        self.assertIn('LazyReference', repr(oc.animal))
 | 
			
		||||
        self.assertIn("LazyReference", repr(oc.animal))
 | 
			
		||||
 | 
			
		||||
    def test___getattr___unknown_attr_raises_attribute_error(self):
 | 
			
		||||
        class Animal(Document):
 | 
			
		||||
@@ -93,7 +93,7 @@ class TestLazyReferenceField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_lazy_reference_set(self):
 | 
			
		||||
        class Animal(Document):
 | 
			
		||||
            meta = {'allow_inheritance': True}
 | 
			
		||||
            meta = {"allow_inheritance": True}
 | 
			
		||||
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            tag = StringField()
 | 
			
		||||
@@ -109,18 +109,17 @@ class TestLazyReferenceField(MongoDBTestCase):
 | 
			
		||||
            nick = StringField()
 | 
			
		||||
 | 
			
		||||
        animal = Animal(name="Leopard", tag="heavy").save()
 | 
			
		||||
        sub_animal = SubAnimal(nick='doggo', name='dog').save()
 | 
			
		||||
        sub_animal = SubAnimal(nick="doggo", name="dog").save()
 | 
			
		||||
        for ref in (
 | 
			
		||||
                animal,
 | 
			
		||||
                animal.pk,
 | 
			
		||||
                DBRef(animal._get_collection_name(), animal.pk),
 | 
			
		||||
                LazyReference(Animal, animal.pk),
 | 
			
		||||
 | 
			
		||||
                sub_animal,
 | 
			
		||||
                sub_animal.pk,
 | 
			
		||||
                DBRef(sub_animal._get_collection_name(), sub_animal.pk),
 | 
			
		||||
                LazyReference(SubAnimal, sub_animal.pk),
 | 
			
		||||
                ):
 | 
			
		||||
            animal,
 | 
			
		||||
            animal.pk,
 | 
			
		||||
            DBRef(animal._get_collection_name(), animal.pk),
 | 
			
		||||
            LazyReference(Animal, animal.pk),
 | 
			
		||||
            sub_animal,
 | 
			
		||||
            sub_animal.pk,
 | 
			
		||||
            DBRef(sub_animal._get_collection_name(), sub_animal.pk),
 | 
			
		||||
            LazyReference(SubAnimal, sub_animal.pk),
 | 
			
		||||
        ):
 | 
			
		||||
            p = Ocurrence(person="test", animal=ref).save()
 | 
			
		||||
            p.reload()
 | 
			
		||||
            self.assertIsInstance(p.animal, LazyReference)
 | 
			
		||||
@@ -144,12 +143,12 @@ class TestLazyReferenceField(MongoDBTestCase):
 | 
			
		||||
        animal = Animal(name="Leopard", tag="heavy").save()
 | 
			
		||||
        baddoc = BadDoc().save()
 | 
			
		||||
        for bad in (
 | 
			
		||||
                42,
 | 
			
		||||
                'foo',
 | 
			
		||||
                baddoc,
 | 
			
		||||
                DBRef(baddoc._get_collection_name(), animal.pk),
 | 
			
		||||
                LazyReference(BadDoc, animal.pk)
 | 
			
		||||
                ):
 | 
			
		||||
            42,
 | 
			
		||||
            "foo",
 | 
			
		||||
            baddoc,
 | 
			
		||||
            DBRef(baddoc._get_collection_name(), animal.pk),
 | 
			
		||||
            LazyReference(BadDoc, animal.pk),
 | 
			
		||||
        ):
 | 
			
		||||
            with self.assertRaises(ValidationError):
 | 
			
		||||
                p = Ocurrence(person="test", animal=bad).save()
 | 
			
		||||
 | 
			
		||||
@@ -157,6 +156,7 @@ class TestLazyReferenceField(MongoDBTestCase):
 | 
			
		||||
        """Ensure that LazyReferenceFields can be queried using objects and values
 | 
			
		||||
        of the type of the primary key of the referenced object.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Member(Document):
 | 
			
		||||
            user_num = IntField(primary_key=True)
 | 
			
		||||
 | 
			
		||||
@@ -172,10 +172,10 @@ class TestLazyReferenceField(MongoDBTestCase):
 | 
			
		||||
        m2 = Member(user_num=2)
 | 
			
		||||
        m2.save()
 | 
			
		||||
 | 
			
		||||
        post1 = BlogPost(title='post 1', author=m1)
 | 
			
		||||
        post1 = BlogPost(title="post 1", author=m1)
 | 
			
		||||
        post1.save()
 | 
			
		||||
 | 
			
		||||
        post2 = BlogPost(title='post 2', author=m2)
 | 
			
		||||
        post2 = BlogPost(title="post 2", author=m2)
 | 
			
		||||
        post2.save()
 | 
			
		||||
 | 
			
		||||
        post = BlogPost.objects(author=m1).first()
 | 
			
		||||
@@ -192,6 +192,7 @@ class TestLazyReferenceField(MongoDBTestCase):
 | 
			
		||||
        """Ensure that LazyReferenceFields can be queried using objects and values
 | 
			
		||||
        of the type of the primary key of the referenced object.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Member(Document):
 | 
			
		||||
            user_num = IntField(primary_key=True)
 | 
			
		||||
 | 
			
		||||
@@ -207,10 +208,10 @@ class TestLazyReferenceField(MongoDBTestCase):
 | 
			
		||||
        m2 = Member(user_num=2)
 | 
			
		||||
        m2.save()
 | 
			
		||||
 | 
			
		||||
        post1 = BlogPost(title='post 1', author=m1)
 | 
			
		||||
        post1 = BlogPost(title="post 1", author=m1)
 | 
			
		||||
        post1.save()
 | 
			
		||||
 | 
			
		||||
        post2 = BlogPost(title='post 2', author=m2)
 | 
			
		||||
        post2 = BlogPost(title="post 2", author=m2)
 | 
			
		||||
        post2.save()
 | 
			
		||||
 | 
			
		||||
        post = BlogPost.objects(author=m1).first()
 | 
			
		||||
@@ -240,19 +241,19 @@ class TestLazyReferenceField(MongoDBTestCase):
 | 
			
		||||
        p = Ocurrence.objects.get()
 | 
			
		||||
        self.assertIsInstance(p.animal, LazyReference)
 | 
			
		||||
        with self.assertRaises(KeyError):
 | 
			
		||||
            p.animal['name']
 | 
			
		||||
            p.animal["name"]
 | 
			
		||||
        with self.assertRaises(AttributeError):
 | 
			
		||||
            p.animal.name
 | 
			
		||||
        self.assertEqual(p.animal.pk, animal.pk)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(p.animal_passthrough.name, "Leopard")
 | 
			
		||||
        self.assertEqual(p.animal_passthrough['name'], "Leopard")
 | 
			
		||||
        self.assertEqual(p.animal_passthrough["name"], "Leopard")
 | 
			
		||||
 | 
			
		||||
        # Should not be able to access referenced document's methods
 | 
			
		||||
        with self.assertRaises(AttributeError):
 | 
			
		||||
            p.animal.save
 | 
			
		||||
        with self.assertRaises(KeyError):
 | 
			
		||||
            p.animal['save']
 | 
			
		||||
            p.animal["save"]
 | 
			
		||||
 | 
			
		||||
    def test_lazy_reference_not_set(self):
 | 
			
		||||
        class Animal(Document):
 | 
			
		||||
@@ -266,7 +267,7 @@ class TestLazyReferenceField(MongoDBTestCase):
 | 
			
		||||
        Animal.drop_collection()
 | 
			
		||||
        Ocurrence.drop_collection()
 | 
			
		||||
 | 
			
		||||
        Ocurrence(person='foo').save()
 | 
			
		||||
        Ocurrence(person="foo").save()
 | 
			
		||||
        p = Ocurrence.objects.get()
 | 
			
		||||
        self.assertIs(p.animal, None)
 | 
			
		||||
 | 
			
		||||
@@ -303,8 +304,8 @@ class TestLazyReferenceField(MongoDBTestCase):
 | 
			
		||||
        Animal.drop_collection()
 | 
			
		||||
        Ocurrence.drop_collection()
 | 
			
		||||
 | 
			
		||||
        animal1 = Animal(name='doggo').save()
 | 
			
		||||
        animal2 = Animal(name='cheeta').save()
 | 
			
		||||
        animal1 = Animal(name="doggo").save()
 | 
			
		||||
        animal2 = Animal(name="cheeta").save()
 | 
			
		||||
 | 
			
		||||
        def check_fields_type(occ):
 | 
			
		||||
            self.assertIsInstance(occ.direct, LazyReference)
 | 
			
		||||
@@ -316,8 +317,8 @@ class TestLazyReferenceField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        occ = Ocurrence(
 | 
			
		||||
            in_list=[animal1, animal2],
 | 
			
		||||
            in_embedded={'in_list': [animal1, animal2], 'direct': animal1},
 | 
			
		||||
            direct=animal1
 | 
			
		||||
            in_embedded={"in_list": [animal1, animal2], "direct": animal1},
 | 
			
		||||
            direct=animal1,
 | 
			
		||||
        ).save()
 | 
			
		||||
        check_fields_type(occ)
 | 
			
		||||
        occ.reload()
 | 
			
		||||
@@ -403,7 +404,7 @@ class TestGenericLazyReferenceField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_generic_lazy_reference_set(self):
 | 
			
		||||
        class Animal(Document):
 | 
			
		||||
            meta = {'allow_inheritance': True}
 | 
			
		||||
            meta = {"allow_inheritance": True}
 | 
			
		||||
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            tag = StringField()
 | 
			
		||||
@@ -419,16 +420,18 @@ class TestGenericLazyReferenceField(MongoDBTestCase):
 | 
			
		||||
            nick = StringField()
 | 
			
		||||
 | 
			
		||||
        animal = Animal(name="Leopard", tag="heavy").save()
 | 
			
		||||
        sub_animal = SubAnimal(nick='doggo', name='dog').save()
 | 
			
		||||
        sub_animal = SubAnimal(nick="doggo", name="dog").save()
 | 
			
		||||
        for ref in (
 | 
			
		||||
                animal,
 | 
			
		||||
                LazyReference(Animal, animal.pk),
 | 
			
		||||
                {'_cls': 'Animal', '_ref': DBRef(animal._get_collection_name(), animal.pk)},
 | 
			
		||||
 | 
			
		||||
                sub_animal,
 | 
			
		||||
                LazyReference(SubAnimal, sub_animal.pk),
 | 
			
		||||
                {'_cls': 'SubAnimal', '_ref': DBRef(sub_animal._get_collection_name(), sub_animal.pk)},
 | 
			
		||||
                ):
 | 
			
		||||
            animal,
 | 
			
		||||
            LazyReference(Animal, animal.pk),
 | 
			
		||||
            {"_cls": "Animal", "_ref": DBRef(animal._get_collection_name(), animal.pk)},
 | 
			
		||||
            sub_animal,
 | 
			
		||||
            LazyReference(SubAnimal, sub_animal.pk),
 | 
			
		||||
            {
 | 
			
		||||
                "_cls": "SubAnimal",
 | 
			
		||||
                "_ref": DBRef(sub_animal._get_collection_name(), sub_animal.pk),
 | 
			
		||||
            },
 | 
			
		||||
        ):
 | 
			
		||||
            p = Ocurrence(person="test", animal=ref).save()
 | 
			
		||||
            p.reload()
 | 
			
		||||
            self.assertIsInstance(p.animal, (LazyReference, Document))
 | 
			
		||||
@@ -441,7 +444,7 @@ class TestGenericLazyReferenceField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        class Ocurrence(Document):
 | 
			
		||||
            person = StringField()
 | 
			
		||||
            animal = GenericLazyReferenceField(choices=['Animal'])
 | 
			
		||||
            animal = GenericLazyReferenceField(choices=["Animal"])
 | 
			
		||||
 | 
			
		||||
        Animal.drop_collection()
 | 
			
		||||
        Ocurrence.drop_collection()
 | 
			
		||||
@@ -451,12 +454,7 @@ class TestGenericLazyReferenceField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        animal = Animal(name="Leopard", tag="heavy").save()
 | 
			
		||||
        baddoc = BadDoc().save()
 | 
			
		||||
        for bad in (
 | 
			
		||||
                42,
 | 
			
		||||
                'foo',
 | 
			
		||||
                baddoc,
 | 
			
		||||
                LazyReference(BadDoc, animal.pk)
 | 
			
		||||
                ):
 | 
			
		||||
        for bad in (42, "foo", baddoc, LazyReference(BadDoc, animal.pk)):
 | 
			
		||||
            with self.assertRaises(ValidationError):
 | 
			
		||||
                p = Ocurrence(person="test", animal=bad).save()
 | 
			
		||||
 | 
			
		||||
@@ -476,10 +474,10 @@ class TestGenericLazyReferenceField(MongoDBTestCase):
 | 
			
		||||
        m2 = Member(user_num=2)
 | 
			
		||||
        m2.save()
 | 
			
		||||
 | 
			
		||||
        post1 = BlogPost(title='post 1', author=m1)
 | 
			
		||||
        post1 = BlogPost(title="post 1", author=m1)
 | 
			
		||||
        post1.save()
 | 
			
		||||
 | 
			
		||||
        post2 = BlogPost(title='post 2', author=m2)
 | 
			
		||||
        post2 = BlogPost(title="post 2", author=m2)
 | 
			
		||||
        post2.save()
 | 
			
		||||
 | 
			
		||||
        post = BlogPost.objects(author=m1).first()
 | 
			
		||||
@@ -504,7 +502,7 @@ class TestGenericLazyReferenceField(MongoDBTestCase):
 | 
			
		||||
        Animal.drop_collection()
 | 
			
		||||
        Ocurrence.drop_collection()
 | 
			
		||||
 | 
			
		||||
        Ocurrence(person='foo').save()
 | 
			
		||||
        Ocurrence(person="foo").save()
 | 
			
		||||
        p = Ocurrence.objects.get()
 | 
			
		||||
        self.assertIs(p.animal, None)
 | 
			
		||||
 | 
			
		||||
@@ -515,7 +513,7 @@ class TestGenericLazyReferenceField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        class Ocurrence(Document):
 | 
			
		||||
            person = StringField()
 | 
			
		||||
            animal = GenericLazyReferenceField('Animal')
 | 
			
		||||
            animal = GenericLazyReferenceField("Animal")
 | 
			
		||||
 | 
			
		||||
        Animal.drop_collection()
 | 
			
		||||
        Ocurrence.drop_collection()
 | 
			
		||||
@@ -542,8 +540,8 @@ class TestGenericLazyReferenceField(MongoDBTestCase):
 | 
			
		||||
        Animal.drop_collection()
 | 
			
		||||
        Ocurrence.drop_collection()
 | 
			
		||||
 | 
			
		||||
        animal1 = Animal(name='doggo').save()
 | 
			
		||||
        animal2 = Animal(name='cheeta').save()
 | 
			
		||||
        animal1 = Animal(name="doggo").save()
 | 
			
		||||
        animal2 = Animal(name="cheeta").save()
 | 
			
		||||
 | 
			
		||||
        def check_fields_type(occ):
 | 
			
		||||
            self.assertIsInstance(occ.direct, LazyReference)
 | 
			
		||||
@@ -555,14 +553,20 @@ class TestGenericLazyReferenceField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        occ = Ocurrence(
 | 
			
		||||
            in_list=[animal1, animal2],
 | 
			
		||||
            in_embedded={'in_list': [animal1, animal2], 'direct': animal1},
 | 
			
		||||
            direct=animal1
 | 
			
		||||
            in_embedded={"in_list": [animal1, animal2], "direct": animal1},
 | 
			
		||||
            direct=animal1,
 | 
			
		||||
        ).save()
 | 
			
		||||
        check_fields_type(occ)
 | 
			
		||||
        occ.reload()
 | 
			
		||||
        check_fields_type(occ)
 | 
			
		||||
        animal1_ref = {'_cls': 'Animal', '_ref': DBRef(animal1._get_collection_name(), animal1.pk)}
 | 
			
		||||
        animal2_ref = {'_cls': 'Animal', '_ref': DBRef(animal2._get_collection_name(), animal2.pk)}
 | 
			
		||||
        animal1_ref = {
 | 
			
		||||
            "_cls": "Animal",
 | 
			
		||||
            "_ref": DBRef(animal1._get_collection_name(), animal1.pk),
 | 
			
		||||
        }
 | 
			
		||||
        animal2_ref = {
 | 
			
		||||
            "_cls": "Animal",
 | 
			
		||||
            "_ref": DBRef(animal2._get_collection_name(), animal2.pk),
 | 
			
		||||
        }
 | 
			
		||||
        occ.direct = animal1_ref
 | 
			
		||||
        occ.in_list = [animal1_ref, animal2_ref]
 | 
			
		||||
        occ.in_embedded.direct = animal1_ref
 | 
			
		||||
 
 | 
			
		||||
@@ -13,23 +13,26 @@ from tests.utils import MongoDBTestCase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestLongField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_long_field_is_considered_as_int64(self):
 | 
			
		||||
        """
 | 
			
		||||
        Tests that long fields are stored as long in mongo, even if long
 | 
			
		||||
        value is small enough to be an int.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class TestLongFieldConsideredAsInt64(Document):
 | 
			
		||||
            some_long = LongField()
 | 
			
		||||
 | 
			
		||||
        doc = TestLongFieldConsideredAsInt64(some_long=42).save()
 | 
			
		||||
        db = get_db()
 | 
			
		||||
        self.assertIsInstance(db.test_long_field_considered_as_int64.find()[0]['some_long'], Int64)
 | 
			
		||||
        self.assertIsInstance(
 | 
			
		||||
            db.test_long_field_considered_as_int64.find()[0]["some_long"], Int64
 | 
			
		||||
        )
 | 
			
		||||
        self.assertIsInstance(doc.some_long, six.integer_types)
 | 
			
		||||
 | 
			
		||||
    def test_long_validation(self):
 | 
			
		||||
        """Ensure that invalid values cannot be assigned to long fields.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class TestDocument(Document):
 | 
			
		||||
            value = LongField(min_value=0, max_value=110)
 | 
			
		||||
 | 
			
		||||
@@ -41,7 +44,7 @@ class TestLongField(MongoDBTestCase):
 | 
			
		||||
        self.assertRaises(ValidationError, doc.validate)
 | 
			
		||||
        doc.value = 120
 | 
			
		||||
        self.assertRaises(ValidationError, doc.validate)
 | 
			
		||||
        doc.value = 'ten'
 | 
			
		||||
        doc.value = "ten"
 | 
			
		||||
        self.assertRaises(ValidationError, doc.validate)
 | 
			
		||||
 | 
			
		||||
    def test_long_ne_operator(self):
 | 
			
		||||
 
 | 
			
		||||
@@ -7,23 +7,24 @@ from tests.utils import MongoDBTestCase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestMapField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_mapfield(self):
 | 
			
		||||
        """Ensure that the MapField handles the declared type."""
 | 
			
		||||
 | 
			
		||||
        class Simple(Document):
 | 
			
		||||
            mapping = MapField(IntField())
 | 
			
		||||
 | 
			
		||||
        Simple.drop_collection()
 | 
			
		||||
 | 
			
		||||
        e = Simple()
 | 
			
		||||
        e.mapping['someint'] = 1
 | 
			
		||||
        e.mapping["someint"] = 1
 | 
			
		||||
        e.save()
 | 
			
		||||
 | 
			
		||||
        with self.assertRaises(ValidationError):
 | 
			
		||||
            e.mapping['somestring'] = "abc"
 | 
			
		||||
            e.mapping["somestring"] = "abc"
 | 
			
		||||
            e.save()
 | 
			
		||||
 | 
			
		||||
        with self.assertRaises(ValidationError):
 | 
			
		||||
 | 
			
		||||
            class NoDeclaredType(Document):
 | 
			
		||||
                mapping = MapField()
 | 
			
		||||
 | 
			
		||||
@@ -45,38 +46,37 @@ class TestMapField(MongoDBTestCase):
 | 
			
		||||
        Extensible.drop_collection()
 | 
			
		||||
 | 
			
		||||
        e = Extensible()
 | 
			
		||||
        e.mapping['somestring'] = StringSetting(value='foo')
 | 
			
		||||
        e.mapping['someint'] = IntegerSetting(value=42)
 | 
			
		||||
        e.mapping["somestring"] = StringSetting(value="foo")
 | 
			
		||||
        e.mapping["someint"] = IntegerSetting(value=42)
 | 
			
		||||
        e.save()
 | 
			
		||||
 | 
			
		||||
        e2 = Extensible.objects.get(id=e.id)
 | 
			
		||||
        self.assertIsInstance(e2.mapping['somestring'], StringSetting)
 | 
			
		||||
        self.assertIsInstance(e2.mapping['someint'], IntegerSetting)
 | 
			
		||||
        self.assertIsInstance(e2.mapping["somestring"], StringSetting)
 | 
			
		||||
        self.assertIsInstance(e2.mapping["someint"], IntegerSetting)
 | 
			
		||||
 | 
			
		||||
        with self.assertRaises(ValidationError):
 | 
			
		||||
            e.mapping['someint'] = 123
 | 
			
		||||
            e.mapping["someint"] = 123
 | 
			
		||||
            e.save()
 | 
			
		||||
 | 
			
		||||
    def test_embedded_mapfield_db_field(self):
 | 
			
		||||
        class Embedded(EmbeddedDocument):
 | 
			
		||||
            number = IntField(default=0, db_field='i')
 | 
			
		||||
            number = IntField(default=0, db_field="i")
 | 
			
		||||
 | 
			
		||||
        class Test(Document):
 | 
			
		||||
            my_map = MapField(field=EmbeddedDocumentField(Embedded),
 | 
			
		||||
                              db_field='x')
 | 
			
		||||
            my_map = MapField(field=EmbeddedDocumentField(Embedded), db_field="x")
 | 
			
		||||
 | 
			
		||||
        Test.drop_collection()
 | 
			
		||||
 | 
			
		||||
        test = Test()
 | 
			
		||||
        test.my_map['DICTIONARY_KEY'] = Embedded(number=1)
 | 
			
		||||
        test.my_map["DICTIONARY_KEY"] = Embedded(number=1)
 | 
			
		||||
        test.save()
 | 
			
		||||
 | 
			
		||||
        Test.objects.update_one(inc__my_map__DICTIONARY_KEY__number=1)
 | 
			
		||||
 | 
			
		||||
        test = Test.objects.get()
 | 
			
		||||
        self.assertEqual(test.my_map['DICTIONARY_KEY'].number, 2)
 | 
			
		||||
        self.assertEqual(test.my_map["DICTIONARY_KEY"].number, 2)
 | 
			
		||||
        doc = self.db.test.find_one()
 | 
			
		||||
        self.assertEqual(doc['x']['DICTIONARY_KEY']['i'], 2)
 | 
			
		||||
        self.assertEqual(doc["x"]["DICTIONARY_KEY"]["i"], 2)
 | 
			
		||||
 | 
			
		||||
    def test_mapfield_numerical_index(self):
 | 
			
		||||
        """Ensure that MapField accept numeric strings as indexes."""
 | 
			
		||||
@@ -90,9 +90,9 @@ class TestMapField(MongoDBTestCase):
 | 
			
		||||
        Test.drop_collection()
 | 
			
		||||
 | 
			
		||||
        test = Test()
 | 
			
		||||
        test.my_map['1'] = Embedded(name='test')
 | 
			
		||||
        test.my_map["1"] = Embedded(name="test")
 | 
			
		||||
        test.save()
 | 
			
		||||
        test.my_map['1'].name = 'test updated'
 | 
			
		||||
        test.my_map["1"].name = "test updated"
 | 
			
		||||
        test.save()
 | 
			
		||||
 | 
			
		||||
    def test_map_field_lookup(self):
 | 
			
		||||
@@ -110,15 +110,20 @@ class TestMapField(MongoDBTestCase):
 | 
			
		||||
            actions = MapField(EmbeddedDocumentField(Action))
 | 
			
		||||
 | 
			
		||||
        Log.drop_collection()
 | 
			
		||||
        Log(name="wilson", visited={'friends': datetime.datetime.now()},
 | 
			
		||||
            actions={'friends': Action(operation='drink', object='beer')}).save()
 | 
			
		||||
        Log(
 | 
			
		||||
            name="wilson",
 | 
			
		||||
            visited={"friends": datetime.datetime.now()},
 | 
			
		||||
            actions={"friends": Action(operation="drink", object="beer")},
 | 
			
		||||
        ).save()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(1, Log.objects(
 | 
			
		||||
            visited__friends__exists=True).count())
 | 
			
		||||
        self.assertEqual(1, Log.objects(visited__friends__exists=True).count())
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(1, Log.objects(
 | 
			
		||||
            actions__friends__operation='drink',
 | 
			
		||||
            actions__friends__object='beer').count())
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            1,
 | 
			
		||||
            Log.objects(
 | 
			
		||||
                actions__friends__operation="drink", actions__friends__object="beer"
 | 
			
		||||
            ).count(),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_map_field_unicode(self):
 | 
			
		||||
        class Info(EmbeddedDocument):
 | 
			
		||||
@@ -130,15 +135,11 @@ class TestMapField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        BlogPost.drop_collection()
 | 
			
		||||
 | 
			
		||||
        tree = BlogPost(info_dict={
 | 
			
		||||
            u"éééé": {
 | 
			
		||||
                'description': u"VALUE: éééé"
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        tree = BlogPost(info_dict={u"éééé": {"description": u"VALUE: éééé"}})
 | 
			
		||||
 | 
			
		||||
        tree.save()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            BlogPost.objects.get(id=tree.id).info_dict[u"éééé"].description,
 | 
			
		||||
            u"VALUE: éééé"
 | 
			
		||||
            u"VALUE: éééé",
 | 
			
		||||
        )
 | 
			
		||||
 
 | 
			
		||||
@@ -26,15 +26,15 @@ class TestReferenceField(MongoDBTestCase):
 | 
			
		||||
        # with a document class name.
 | 
			
		||||
        self.assertRaises(ValidationError, ReferenceField, EmbeddedDocument)
 | 
			
		||||
 | 
			
		||||
        user = User(name='Test User')
 | 
			
		||||
        user = User(name="Test User")
 | 
			
		||||
 | 
			
		||||
        # Ensure that the referenced object must have been saved
 | 
			
		||||
        post1 = BlogPost(content='Chips and gravy taste good.')
 | 
			
		||||
        post1 = BlogPost(content="Chips and gravy taste good.")
 | 
			
		||||
        post1.author = user
 | 
			
		||||
        self.assertRaises(ValidationError, post1.save)
 | 
			
		||||
 | 
			
		||||
        # Check that an invalid object type cannot be used
 | 
			
		||||
        post2 = BlogPost(content='Chips and chilli taste good.')
 | 
			
		||||
        post2 = BlogPost(content="Chips and chilli taste good.")
 | 
			
		||||
        post1.author = post2
 | 
			
		||||
        self.assertRaises(ValidationError, post1.validate)
 | 
			
		||||
 | 
			
		||||
@@ -59,7 +59,7 @@ class TestReferenceField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            parent = ReferenceField('self')
 | 
			
		||||
            parent = ReferenceField("self")
 | 
			
		||||
 | 
			
		||||
        Person.drop_collection()
 | 
			
		||||
 | 
			
		||||
@@ -74,7 +74,7 @@ class TestReferenceField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            parent = ReferenceField('self', dbref=True)
 | 
			
		||||
            parent = ReferenceField("self", dbref=True)
 | 
			
		||||
 | 
			
		||||
        Person.drop_collection()
 | 
			
		||||
 | 
			
		||||
@@ -82,8 +82,8 @@ class TestReferenceField(MongoDBTestCase):
 | 
			
		||||
        Person(name="Ross", parent=p1).save()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            Person._get_collection().find_one({'name': 'Ross'})['parent'],
 | 
			
		||||
            DBRef('person', p1.pk)
 | 
			
		||||
            Person._get_collection().find_one({"name": "Ross"})["parent"],
 | 
			
		||||
            DBRef("person", p1.pk),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        p = Person.objects.get(name="Ross")
 | 
			
		||||
@@ -97,21 +97,17 @@ class TestReferenceField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            parent = ReferenceField('self', dbref=False)
 | 
			
		||||
            parent = ReferenceField("self", dbref=False)
 | 
			
		||||
 | 
			
		||||
        p = Person(
 | 
			
		||||
            name='Steve',
 | 
			
		||||
            parent=DBRef('person', 'abcdefghijklmnop')
 | 
			
		||||
        p = Person(name="Steve", parent=DBRef("person", "abcdefghijklmnop"))
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            p.to_mongo(), SON([("name", u"Steve"), ("parent", "abcdefghijklmnop")])
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(p.to_mongo(), SON([
 | 
			
		||||
            ('name', u'Steve'),
 | 
			
		||||
            ('parent', 'abcdefghijklmnop')
 | 
			
		||||
        ]))
 | 
			
		||||
 | 
			
		||||
    def test_objectid_reference_fields(self):
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            parent = ReferenceField('self', dbref=False)
 | 
			
		||||
            parent = ReferenceField("self", dbref=False)
 | 
			
		||||
 | 
			
		||||
        Person.drop_collection()
 | 
			
		||||
 | 
			
		||||
@@ -119,8 +115,8 @@ class TestReferenceField(MongoDBTestCase):
 | 
			
		||||
        Person(name="Ross", parent=p1).save()
 | 
			
		||||
 | 
			
		||||
        col = Person._get_collection()
 | 
			
		||||
        data = col.find_one({'name': 'Ross'})
 | 
			
		||||
        self.assertEqual(data['parent'], p1.pk)
 | 
			
		||||
        data = col.find_one({"name": "Ross"})
 | 
			
		||||
        self.assertEqual(data["parent"], p1.pk)
 | 
			
		||||
 | 
			
		||||
        p = Person.objects.get(name="Ross")
 | 
			
		||||
        self.assertEqual(p.parent, p1)
 | 
			
		||||
@@ -128,9 +124,10 @@ class TestReferenceField(MongoDBTestCase):
 | 
			
		||||
    def test_undefined_reference(self):
 | 
			
		||||
        """Ensure that ReferenceFields may reference undefined Documents.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Product(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            company = ReferenceField('Company')
 | 
			
		||||
            company = ReferenceField("Company")
 | 
			
		||||
 | 
			
		||||
        class Company(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
@@ -138,12 +135,12 @@ class TestReferenceField(MongoDBTestCase):
 | 
			
		||||
        Product.drop_collection()
 | 
			
		||||
        Company.drop_collection()
 | 
			
		||||
 | 
			
		||||
        ten_gen = Company(name='10gen')
 | 
			
		||||
        ten_gen = Company(name="10gen")
 | 
			
		||||
        ten_gen.save()
 | 
			
		||||
        mongodb = Product(name='MongoDB', company=ten_gen)
 | 
			
		||||
        mongodb = Product(name="MongoDB", company=ten_gen)
 | 
			
		||||
        mongodb.save()
 | 
			
		||||
 | 
			
		||||
        me = Product(name='MongoEngine')
 | 
			
		||||
        me = Product(name="MongoEngine")
 | 
			
		||||
        me.save()
 | 
			
		||||
 | 
			
		||||
        obj = Product.objects(company=ten_gen).first()
 | 
			
		||||
@@ -160,6 +157,7 @@ class TestReferenceField(MongoDBTestCase):
 | 
			
		||||
        """Ensure that ReferenceFields can be queried using objects and values
 | 
			
		||||
        of the type of the primary key of the referenced object.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Member(Document):
 | 
			
		||||
            user_num = IntField(primary_key=True)
 | 
			
		||||
 | 
			
		||||
@@ -175,10 +173,10 @@ class TestReferenceField(MongoDBTestCase):
 | 
			
		||||
        m2 = Member(user_num=2)
 | 
			
		||||
        m2.save()
 | 
			
		||||
 | 
			
		||||
        post1 = BlogPost(title='post 1', author=m1)
 | 
			
		||||
        post1 = BlogPost(title="post 1", author=m1)
 | 
			
		||||
        post1.save()
 | 
			
		||||
 | 
			
		||||
        post2 = BlogPost(title='post 2', author=m2)
 | 
			
		||||
        post2 = BlogPost(title="post 2", author=m2)
 | 
			
		||||
        post2.save()
 | 
			
		||||
 | 
			
		||||
        post = BlogPost.objects(author=m1).first()
 | 
			
		||||
@@ -191,6 +189,7 @@ class TestReferenceField(MongoDBTestCase):
 | 
			
		||||
        """Ensure that ReferenceFields can be queried using objects and values
 | 
			
		||||
        of the type of the primary key of the referenced object.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Member(Document):
 | 
			
		||||
            user_num = IntField(primary_key=True)
 | 
			
		||||
 | 
			
		||||
@@ -206,10 +205,10 @@ class TestReferenceField(MongoDBTestCase):
 | 
			
		||||
        m2 = Member(user_num=2)
 | 
			
		||||
        m2.save()
 | 
			
		||||
 | 
			
		||||
        post1 = BlogPost(title='post 1', author=m1)
 | 
			
		||||
        post1 = BlogPost(title="post 1", author=m1)
 | 
			
		||||
        post1.save()
 | 
			
		||||
 | 
			
		||||
        post2 = BlogPost(title='post 2', author=m2)
 | 
			
		||||
        post2 = BlogPost(title="post 2", author=m2)
 | 
			
		||||
        post2.save()
 | 
			
		||||
 | 
			
		||||
        post = BlogPost.objects(author=m1).first()
 | 
			
		||||
 
 | 
			
		||||
@@ -11,38 +11,38 @@ class TestSequenceField(MongoDBTestCase):
 | 
			
		||||
            id = SequenceField(primary_key=True)
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
        self.db['mongoengine.counters'].drop()
 | 
			
		||||
        self.db["mongoengine.counters"].drop()
 | 
			
		||||
        Person.drop_collection()
 | 
			
		||||
 | 
			
		||||
        for x in range(10):
 | 
			
		||||
            Person(name="Person %s" % x).save()
 | 
			
		||||
 | 
			
		||||
        c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'})
 | 
			
		||||
        self.assertEqual(c['next'], 10)
 | 
			
		||||
        c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
 | 
			
		||||
        self.assertEqual(c["next"], 10)
 | 
			
		||||
 | 
			
		||||
        ids = [i.id for i in Person.objects]
 | 
			
		||||
        self.assertEqual(ids, range(1, 11))
 | 
			
		||||
 | 
			
		||||
        c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'})
 | 
			
		||||
        self.assertEqual(c['next'], 10)
 | 
			
		||||
        c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
 | 
			
		||||
        self.assertEqual(c["next"], 10)
 | 
			
		||||
 | 
			
		||||
        Person.id.set_next_value(1000)
 | 
			
		||||
        c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'})
 | 
			
		||||
        self.assertEqual(c['next'], 1000)
 | 
			
		||||
        c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
 | 
			
		||||
        self.assertEqual(c["next"], 1000)
 | 
			
		||||
 | 
			
		||||
    def test_sequence_field_get_next_value(self):
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            id = SequenceField(primary_key=True)
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
        self.db['mongoengine.counters'].drop()
 | 
			
		||||
        self.db["mongoengine.counters"].drop()
 | 
			
		||||
        Person.drop_collection()
 | 
			
		||||
 | 
			
		||||
        for x in range(10):
 | 
			
		||||
            Person(name="Person %s" % x).save()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(Person.id.get_next_value(), 11)
 | 
			
		||||
        self.db['mongoengine.counters'].drop()
 | 
			
		||||
        self.db["mongoengine.counters"].drop()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(Person.id.get_next_value(), 1)
 | 
			
		||||
 | 
			
		||||
@@ -50,40 +50,40 @@ class TestSequenceField(MongoDBTestCase):
 | 
			
		||||
            id = SequenceField(primary_key=True, value_decorator=str)
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
        self.db['mongoengine.counters'].drop()
 | 
			
		||||
        self.db["mongoengine.counters"].drop()
 | 
			
		||||
        Person.drop_collection()
 | 
			
		||||
 | 
			
		||||
        for x in range(10):
 | 
			
		||||
            Person(name="Person %s" % x).save()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(Person.id.get_next_value(), '11')
 | 
			
		||||
        self.db['mongoengine.counters'].drop()
 | 
			
		||||
        self.assertEqual(Person.id.get_next_value(), "11")
 | 
			
		||||
        self.db["mongoengine.counters"].drop()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(Person.id.get_next_value(), '1')
 | 
			
		||||
        self.assertEqual(Person.id.get_next_value(), "1")
 | 
			
		||||
 | 
			
		||||
    def test_sequence_field_sequence_name(self):
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            id = SequenceField(primary_key=True, sequence_name='jelly')
 | 
			
		||||
            id = SequenceField(primary_key=True, sequence_name="jelly")
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
        self.db['mongoengine.counters'].drop()
 | 
			
		||||
        self.db["mongoengine.counters"].drop()
 | 
			
		||||
        Person.drop_collection()
 | 
			
		||||
 | 
			
		||||
        for x in range(10):
 | 
			
		||||
            Person(name="Person %s" % x).save()
 | 
			
		||||
 | 
			
		||||
        c = self.db['mongoengine.counters'].find_one({'_id': 'jelly.id'})
 | 
			
		||||
        self.assertEqual(c['next'], 10)
 | 
			
		||||
        c = self.db["mongoengine.counters"].find_one({"_id": "jelly.id"})
 | 
			
		||||
        self.assertEqual(c["next"], 10)
 | 
			
		||||
 | 
			
		||||
        ids = [i.id for i in Person.objects]
 | 
			
		||||
        self.assertEqual(ids, range(1, 11))
 | 
			
		||||
 | 
			
		||||
        c = self.db['mongoengine.counters'].find_one({'_id': 'jelly.id'})
 | 
			
		||||
        self.assertEqual(c['next'], 10)
 | 
			
		||||
        c = self.db["mongoengine.counters"].find_one({"_id": "jelly.id"})
 | 
			
		||||
        self.assertEqual(c["next"], 10)
 | 
			
		||||
 | 
			
		||||
        Person.id.set_next_value(1000)
 | 
			
		||||
        c = self.db['mongoengine.counters'].find_one({'_id': 'jelly.id'})
 | 
			
		||||
        self.assertEqual(c['next'], 1000)
 | 
			
		||||
        c = self.db["mongoengine.counters"].find_one({"_id": "jelly.id"})
 | 
			
		||||
        self.assertEqual(c["next"], 1000)
 | 
			
		||||
 | 
			
		||||
    def test_multiple_sequence_fields(self):
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
@@ -91,14 +91,14 @@ class TestSequenceField(MongoDBTestCase):
 | 
			
		||||
            counter = SequenceField()
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
        self.db['mongoengine.counters'].drop()
 | 
			
		||||
        self.db["mongoengine.counters"].drop()
 | 
			
		||||
        Person.drop_collection()
 | 
			
		||||
 | 
			
		||||
        for x in range(10):
 | 
			
		||||
            Person(name="Person %s" % x).save()
 | 
			
		||||
 | 
			
		||||
        c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'})
 | 
			
		||||
        self.assertEqual(c['next'], 10)
 | 
			
		||||
        c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
 | 
			
		||||
        self.assertEqual(c["next"], 10)
 | 
			
		||||
 | 
			
		||||
        ids = [i.id for i in Person.objects]
 | 
			
		||||
        self.assertEqual(ids, range(1, 11))
 | 
			
		||||
@@ -106,23 +106,23 @@ class TestSequenceField(MongoDBTestCase):
 | 
			
		||||
        counters = [i.counter for i in Person.objects]
 | 
			
		||||
        self.assertEqual(counters, range(1, 11))
 | 
			
		||||
 | 
			
		||||
        c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'})
 | 
			
		||||
        self.assertEqual(c['next'], 10)
 | 
			
		||||
        c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
 | 
			
		||||
        self.assertEqual(c["next"], 10)
 | 
			
		||||
 | 
			
		||||
        Person.id.set_next_value(1000)
 | 
			
		||||
        c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'})
 | 
			
		||||
        self.assertEqual(c['next'], 1000)
 | 
			
		||||
        c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
 | 
			
		||||
        self.assertEqual(c["next"], 1000)
 | 
			
		||||
 | 
			
		||||
        Person.counter.set_next_value(999)
 | 
			
		||||
        c = self.db['mongoengine.counters'].find_one({'_id': 'person.counter'})
 | 
			
		||||
        self.assertEqual(c['next'], 999)
 | 
			
		||||
        c = self.db["mongoengine.counters"].find_one({"_id": "person.counter"})
 | 
			
		||||
        self.assertEqual(c["next"], 999)
 | 
			
		||||
 | 
			
		||||
    def test_sequence_fields_reload(self):
 | 
			
		||||
        class Animal(Document):
 | 
			
		||||
            counter = SequenceField()
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
        self.db['mongoengine.counters'].drop()
 | 
			
		||||
        self.db["mongoengine.counters"].drop()
 | 
			
		||||
        Animal.drop_collection()
 | 
			
		||||
 | 
			
		||||
        a = Animal(name="Boi").save()
 | 
			
		||||
@@ -151,7 +151,7 @@ class TestSequenceField(MongoDBTestCase):
 | 
			
		||||
            id = SequenceField(primary_key=True)
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
        self.db['mongoengine.counters'].drop()
 | 
			
		||||
        self.db["mongoengine.counters"].drop()
 | 
			
		||||
        Animal.drop_collection()
 | 
			
		||||
        Person.drop_collection()
 | 
			
		||||
 | 
			
		||||
@@ -159,11 +159,11 @@ class TestSequenceField(MongoDBTestCase):
 | 
			
		||||
            Animal(name="Animal %s" % x).save()
 | 
			
		||||
            Person(name="Person %s" % x).save()
 | 
			
		||||
 | 
			
		||||
        c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'})
 | 
			
		||||
        self.assertEqual(c['next'], 10)
 | 
			
		||||
        c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
 | 
			
		||||
        self.assertEqual(c["next"], 10)
 | 
			
		||||
 | 
			
		||||
        c = self.db['mongoengine.counters'].find_one({'_id': 'animal.id'})
 | 
			
		||||
        self.assertEqual(c['next'], 10)
 | 
			
		||||
        c = self.db["mongoengine.counters"].find_one({"_id": "animal.id"})
 | 
			
		||||
        self.assertEqual(c["next"], 10)
 | 
			
		||||
 | 
			
		||||
        ids = [i.id for i in Person.objects]
 | 
			
		||||
        self.assertEqual(ids, range(1, 11))
 | 
			
		||||
@@ -171,32 +171,32 @@ class TestSequenceField(MongoDBTestCase):
 | 
			
		||||
        id = [i.id for i in Animal.objects]
 | 
			
		||||
        self.assertEqual(id, range(1, 11))
 | 
			
		||||
 | 
			
		||||
        c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'})
 | 
			
		||||
        self.assertEqual(c['next'], 10)
 | 
			
		||||
        c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
 | 
			
		||||
        self.assertEqual(c["next"], 10)
 | 
			
		||||
 | 
			
		||||
        c = self.db['mongoengine.counters'].find_one({'_id': 'animal.id'})
 | 
			
		||||
        self.assertEqual(c['next'], 10)
 | 
			
		||||
        c = self.db["mongoengine.counters"].find_one({"_id": "animal.id"})
 | 
			
		||||
        self.assertEqual(c["next"], 10)
 | 
			
		||||
 | 
			
		||||
    def test_sequence_field_value_decorator(self):
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            id = SequenceField(primary_key=True, value_decorator=str)
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
        self.db['mongoengine.counters'].drop()
 | 
			
		||||
        self.db["mongoengine.counters"].drop()
 | 
			
		||||
        Person.drop_collection()
 | 
			
		||||
 | 
			
		||||
        for x in range(10):
 | 
			
		||||
            p = Person(name="Person %s" % x)
 | 
			
		||||
            p.save()
 | 
			
		||||
 | 
			
		||||
        c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'})
 | 
			
		||||
        self.assertEqual(c['next'], 10)
 | 
			
		||||
        c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
 | 
			
		||||
        self.assertEqual(c["next"], 10)
 | 
			
		||||
 | 
			
		||||
        ids = [i.id for i in Person.objects]
 | 
			
		||||
        self.assertEqual(ids, map(str, range(1, 11)))
 | 
			
		||||
 | 
			
		||||
        c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'})
 | 
			
		||||
        self.assertEqual(c['next'], 10)
 | 
			
		||||
        c = self.db["mongoengine.counters"].find_one({"_id": "person.id"})
 | 
			
		||||
        self.assertEqual(c["next"], 10)
 | 
			
		||||
 | 
			
		||||
    def test_embedded_sequence_field(self):
 | 
			
		||||
        class Comment(EmbeddedDocument):
 | 
			
		||||
@@ -207,14 +207,18 @@ class TestSequenceField(MongoDBTestCase):
 | 
			
		||||
            title = StringField(required=True)
 | 
			
		||||
            comments = ListField(EmbeddedDocumentField(Comment))
 | 
			
		||||
 | 
			
		||||
        self.db['mongoengine.counters'].drop()
 | 
			
		||||
        self.db["mongoengine.counters"].drop()
 | 
			
		||||
        Post.drop_collection()
 | 
			
		||||
 | 
			
		||||
        Post(title="MongoEngine",
 | 
			
		||||
             comments=[Comment(content="NoSQL Rocks"),
 | 
			
		||||
                       Comment(content="MongoEngine Rocks")]).save()
 | 
			
		||||
        c = self.db['mongoengine.counters'].find_one({'_id': 'comment.id'})
 | 
			
		||||
        self.assertEqual(c['next'], 2)
 | 
			
		||||
        Post(
 | 
			
		||||
            title="MongoEngine",
 | 
			
		||||
            comments=[
 | 
			
		||||
                Comment(content="NoSQL Rocks"),
 | 
			
		||||
                Comment(content="MongoEngine Rocks"),
 | 
			
		||||
            ],
 | 
			
		||||
        ).save()
 | 
			
		||||
        c = self.db["mongoengine.counters"].find_one({"_id": "comment.id"})
 | 
			
		||||
        self.assertEqual(c["next"], 2)
 | 
			
		||||
        post = Post.objects.first()
 | 
			
		||||
        self.assertEqual(1, post.comments[0].id)
 | 
			
		||||
        self.assertEqual(2, post.comments[1].id)
 | 
			
		||||
@@ -223,7 +227,7 @@ class TestSequenceField(MongoDBTestCase):
 | 
			
		||||
        class Base(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            counter = SequenceField()
 | 
			
		||||
            meta = {'abstract': True}
 | 
			
		||||
            meta = {"abstract": True}
 | 
			
		||||
 | 
			
		||||
        class Foo(Base):
 | 
			
		||||
            pass
 | 
			
		||||
@@ -231,24 +235,27 @@ class TestSequenceField(MongoDBTestCase):
 | 
			
		||||
        class Bar(Base):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        bar = Bar(name='Bar')
 | 
			
		||||
        bar = Bar(name="Bar")
 | 
			
		||||
        bar.save()
 | 
			
		||||
 | 
			
		||||
        foo = Foo(name='Foo')
 | 
			
		||||
        foo = Foo(name="Foo")
 | 
			
		||||
        foo.save()
 | 
			
		||||
 | 
			
		||||
        self.assertTrue('base.counter' in
 | 
			
		||||
                        self.db['mongoengine.counters'].find().distinct('_id'))
 | 
			
		||||
        self.assertFalse(('foo.counter' or 'bar.counter') in
 | 
			
		||||
                         self.db['mongoengine.counters'].find().distinct('_id'))
 | 
			
		||||
        self.assertTrue(
 | 
			
		||||
            "base.counter" in self.db["mongoengine.counters"].find().distinct("_id")
 | 
			
		||||
        )
 | 
			
		||||
        self.assertFalse(
 | 
			
		||||
            ("foo.counter" or "bar.counter")
 | 
			
		||||
            in self.db["mongoengine.counters"].find().distinct("_id")
 | 
			
		||||
        )
 | 
			
		||||
        self.assertNotEqual(foo.counter, bar.counter)
 | 
			
		||||
        self.assertEqual(foo._fields['counter'].owner_document, Base)
 | 
			
		||||
        self.assertEqual(bar._fields['counter'].owner_document, Base)
 | 
			
		||||
        self.assertEqual(foo._fields["counter"].owner_document, Base)
 | 
			
		||||
        self.assertEqual(bar._fields["counter"].owner_document, Base)
 | 
			
		||||
 | 
			
		||||
    def test_no_inherited_sequencefield(self):
 | 
			
		||||
        class Base(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            meta = {'abstract': True}
 | 
			
		||||
            meta = {"abstract": True}
 | 
			
		||||
 | 
			
		||||
        class Foo(Base):
 | 
			
		||||
            counter = SequenceField()
 | 
			
		||||
@@ -256,16 +263,19 @@ class TestSequenceField(MongoDBTestCase):
 | 
			
		||||
        class Bar(Base):
 | 
			
		||||
            counter = SequenceField()
 | 
			
		||||
 | 
			
		||||
        bar = Bar(name='Bar')
 | 
			
		||||
        bar = Bar(name="Bar")
 | 
			
		||||
        bar.save()
 | 
			
		||||
 | 
			
		||||
        foo = Foo(name='Foo')
 | 
			
		||||
        foo = Foo(name="Foo")
 | 
			
		||||
        foo.save()
 | 
			
		||||
 | 
			
		||||
        self.assertFalse('base.counter' in
 | 
			
		||||
                         self.db['mongoengine.counters'].find().distinct('_id'))
 | 
			
		||||
        self.assertTrue(('foo.counter' and 'bar.counter') in
 | 
			
		||||
                         self.db['mongoengine.counters'].find().distinct('_id'))
 | 
			
		||||
        self.assertFalse(
 | 
			
		||||
            "base.counter" in self.db["mongoengine.counters"].find().distinct("_id")
 | 
			
		||||
        )
 | 
			
		||||
        self.assertTrue(
 | 
			
		||||
            ("foo.counter" and "bar.counter")
 | 
			
		||||
            in self.db["mongoengine.counters"].find().distinct("_id")
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(foo.counter, bar.counter)
 | 
			
		||||
        self.assertEqual(foo._fields['counter'].owner_document, Foo)
 | 
			
		||||
        self.assertEqual(bar._fields['counter'].owner_document, Bar)
 | 
			
		||||
        self.assertEqual(foo._fields["counter"].owner_document, Foo)
 | 
			
		||||
        self.assertEqual(bar._fields["counter"].owner_document, Bar)
 | 
			
		||||
 
 | 
			
		||||
@@ -5,49 +5,53 @@ from tests.utils import MongoDBTestCase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestURLField(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_validation(self):
 | 
			
		||||
        """Ensure that URLFields validate urls properly."""
 | 
			
		||||
 | 
			
		||||
        class Link(Document):
 | 
			
		||||
            url = URLField()
 | 
			
		||||
 | 
			
		||||
        link = Link()
 | 
			
		||||
        link.url = 'google'
 | 
			
		||||
        link.url = "google"
 | 
			
		||||
        self.assertRaises(ValidationError, link.validate)
 | 
			
		||||
 | 
			
		||||
        link.url = 'http://www.google.com:8080'
 | 
			
		||||
        link.url = "http://www.google.com:8080"
 | 
			
		||||
        link.validate()
 | 
			
		||||
 | 
			
		||||
    def test_unicode_url_validation(self):
 | 
			
		||||
        """Ensure unicode URLs are validated properly."""
 | 
			
		||||
 | 
			
		||||
        class Link(Document):
 | 
			
		||||
            url = URLField()
 | 
			
		||||
 | 
			
		||||
        link = Link()
 | 
			
		||||
        link.url = u'http://привет.com'
 | 
			
		||||
        link.url = u"http://привет.com"
 | 
			
		||||
 | 
			
		||||
        # TODO fix URL validation - this *IS* a valid URL
 | 
			
		||||
        # For now we just want to make sure that the error message is correct
 | 
			
		||||
        with self.assertRaises(ValidationError) as ctx_err:
 | 
			
		||||
            link.validate()
 | 
			
		||||
        self.assertEqual(unicode(ctx_err.exception),
 | 
			
		||||
                         u"ValidationError (Link:None) (Invalid URL: http://\u043f\u0440\u0438\u0432\u0435\u0442.com: ['url'])")
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            unicode(ctx_err.exception),
 | 
			
		||||
            u"ValidationError (Link:None) (Invalid URL: http://\u043f\u0440\u0438\u0432\u0435\u0442.com: ['url'])",
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_url_scheme_validation(self):
 | 
			
		||||
        """Ensure that URLFields validate urls with specific schemes properly.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Link(Document):
 | 
			
		||||
            url = URLField()
 | 
			
		||||
 | 
			
		||||
        class SchemeLink(Document):
 | 
			
		||||
            url = URLField(schemes=['ws', 'irc'])
 | 
			
		||||
            url = URLField(schemes=["ws", "irc"])
 | 
			
		||||
 | 
			
		||||
        link = Link()
 | 
			
		||||
        link.url = 'ws://google.com'
 | 
			
		||||
        link.url = "ws://google.com"
 | 
			
		||||
        self.assertRaises(ValidationError, link.validate)
 | 
			
		||||
 | 
			
		||||
        scheme_link = SchemeLink()
 | 
			
		||||
        scheme_link.url = 'ws://google.com'
 | 
			
		||||
        scheme_link.url = "ws://google.com"
 | 
			
		||||
        scheme_link.validate()
 | 
			
		||||
 | 
			
		||||
    def test_underscore_allowed_in_domains_names(self):
 | 
			
		||||
@@ -55,5 +59,5 @@ class TestURLField(MongoDBTestCase):
 | 
			
		||||
            url = URLField()
 | 
			
		||||
 | 
			
		||||
        link = Link()
 | 
			
		||||
        link.url = 'https://san_leandro-ca.geebo.com'
 | 
			
		||||
        link.url = "https://san_leandro-ca.geebo.com"
 | 
			
		||||
        link.validate()
 | 
			
		||||
 
 | 
			
		||||
@@ -15,11 +15,8 @@ class TestUUIDField(MongoDBTestCase):
 | 
			
		||||
        uid = uuid.uuid4()
 | 
			
		||||
        person = Person(api_key=uid).save()
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            get_as_pymongo(person),
 | 
			
		||||
            {'_id': person.id,
 | 
			
		||||
             'api_key': str(uid)
 | 
			
		||||
             }
 | 
			
		||||
            )
 | 
			
		||||
            get_as_pymongo(person), {"_id": person.id, "api_key": str(uid)}
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_field_string(self):
 | 
			
		||||
        """Test UUID fields storing as String
 | 
			
		||||
@@ -37,8 +34,10 @@ class TestUUIDField(MongoDBTestCase):
 | 
			
		||||
            person.api_key = api_key
 | 
			
		||||
            person.validate()
 | 
			
		||||
 | 
			
		||||
        invalid = ('9d159858-549b-4975-9f98-dd2f987c113g',
 | 
			
		||||
                   '9d159858-549b-4975-9f98-dd2f987c113')
 | 
			
		||||
        invalid = (
 | 
			
		||||
            "9d159858-549b-4975-9f98-dd2f987c113g",
 | 
			
		||||
            "9d159858-549b-4975-9f98-dd2f987c113",
 | 
			
		||||
        )
 | 
			
		||||
        for api_key in invalid:
 | 
			
		||||
            person.api_key = api_key
 | 
			
		||||
            self.assertRaises(ValidationError, person.validate)
 | 
			
		||||
@@ -58,8 +57,10 @@ class TestUUIDField(MongoDBTestCase):
 | 
			
		||||
            person.api_key = api_key
 | 
			
		||||
            person.validate()
 | 
			
		||||
 | 
			
		||||
        invalid = ('9d159858-549b-4975-9f98-dd2f987c113g',
 | 
			
		||||
                   '9d159858-549b-4975-9f98-dd2f987c113')
 | 
			
		||||
        invalid = (
 | 
			
		||||
            "9d159858-549b-4975-9f98-dd2f987c113g",
 | 
			
		||||
            "9d159858-549b-4975-9f98-dd2f987c113",
 | 
			
		||||
        )
 | 
			
		||||
        for api_key in invalid:
 | 
			
		||||
            person.api_key = api_key
 | 
			
		||||
            self.assertRaises(ValidationError, person.validate)
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ class PickleEmbedded(EmbeddedDocument):
 | 
			
		||||
 | 
			
		||||
class PickleTest(Document):
 | 
			
		||||
    number = IntField()
 | 
			
		||||
    string = StringField(choices=(('One', '1'), ('Two', '2')))
 | 
			
		||||
    string = StringField(choices=(("One", "1"), ("Two", "2")))
 | 
			
		||||
    embedded = EmbeddedDocumentField(PickleEmbedded)
 | 
			
		||||
    lists = ListField(StringField())
 | 
			
		||||
    photo = FileField()
 | 
			
		||||
@@ -19,7 +19,7 @@ class PickleTest(Document):
 | 
			
		||||
 | 
			
		||||
class NewDocumentPickleTest(Document):
 | 
			
		||||
    number = IntField()
 | 
			
		||||
    string = StringField(choices=(('One', '1'), ('Two', '2')))
 | 
			
		||||
    string = StringField(choices=(("One", "1"), ("Two", "2")))
 | 
			
		||||
    embedded = EmbeddedDocumentField(PickleEmbedded)
 | 
			
		||||
    lists = ListField(StringField())
 | 
			
		||||
    photo = FileField()
 | 
			
		||||
@@ -36,7 +36,7 @@ class PickleDynamicTest(DynamicDocument):
 | 
			
		||||
 | 
			
		||||
class PickleSignalsTest(Document):
 | 
			
		||||
    number = IntField()
 | 
			
		||||
    string = StringField(choices=(('One', '1'), ('Two', '2')))
 | 
			
		||||
    string = StringField(choices=(("One", "1"), ("Two", "2")))
 | 
			
		||||
    embedded = EmbeddedDocumentField(PickleEmbedded)
 | 
			
		||||
    lists = ListField(StringField())
 | 
			
		||||
 | 
			
		||||
@@ -58,4 +58,4 @@ class Mixin(object):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Base(Document):
 | 
			
		||||
    meta = {'allow_inheritance': True}
 | 
			
		||||
    meta = {"allow_inheritance": True}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,79 +7,78 @@ __all__ = ("QueryFieldListTest", "OnlyExcludeAllTest")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class QueryFieldListTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    def test_empty(self):
 | 
			
		||||
        q = QueryFieldList()
 | 
			
		||||
        self.assertFalse(q)
 | 
			
		||||
 | 
			
		||||
        q = QueryFieldList(always_include=['_cls'])
 | 
			
		||||
        q = QueryFieldList(always_include=["_cls"])
 | 
			
		||||
        self.assertFalse(q)
 | 
			
		||||
 | 
			
		||||
    def test_include_include(self):
 | 
			
		||||
        q = QueryFieldList()
 | 
			
		||||
        q += QueryFieldList(fields=['a', 'b'], value=QueryFieldList.ONLY, _only_called=True)
 | 
			
		||||
        self.assertEqual(q.as_dict(), {'a': 1, 'b': 1})
 | 
			
		||||
        q += QueryFieldList(fields=['b', 'c'], value=QueryFieldList.ONLY)
 | 
			
		||||
        self.assertEqual(q.as_dict(), {'a': 1, 'b': 1, 'c': 1})
 | 
			
		||||
        q += QueryFieldList(
 | 
			
		||||
            fields=["a", "b"], value=QueryFieldList.ONLY, _only_called=True
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(q.as_dict(), {"a": 1, "b": 1})
 | 
			
		||||
        q += QueryFieldList(fields=["b", "c"], value=QueryFieldList.ONLY)
 | 
			
		||||
        self.assertEqual(q.as_dict(), {"a": 1, "b": 1, "c": 1})
 | 
			
		||||
 | 
			
		||||
    def test_include_exclude(self):
 | 
			
		||||
        q = QueryFieldList()
 | 
			
		||||
        q += QueryFieldList(fields=['a', 'b'], value=QueryFieldList.ONLY)
 | 
			
		||||
        self.assertEqual(q.as_dict(), {'a': 1, 'b': 1})
 | 
			
		||||
        q += QueryFieldList(fields=['b', 'c'], value=QueryFieldList.EXCLUDE)
 | 
			
		||||
        self.assertEqual(q.as_dict(), {'a': 1})
 | 
			
		||||
        q += QueryFieldList(fields=["a", "b"], value=QueryFieldList.ONLY)
 | 
			
		||||
        self.assertEqual(q.as_dict(), {"a": 1, "b": 1})
 | 
			
		||||
        q += QueryFieldList(fields=["b", "c"], value=QueryFieldList.EXCLUDE)
 | 
			
		||||
        self.assertEqual(q.as_dict(), {"a": 1})
 | 
			
		||||
 | 
			
		||||
    def test_exclude_exclude(self):
 | 
			
		||||
        q = QueryFieldList()
 | 
			
		||||
        q += QueryFieldList(fields=['a', 'b'], value=QueryFieldList.EXCLUDE)
 | 
			
		||||
        self.assertEqual(q.as_dict(), {'a': 0, 'b': 0})
 | 
			
		||||
        q += QueryFieldList(fields=['b', 'c'], value=QueryFieldList.EXCLUDE)
 | 
			
		||||
        self.assertEqual(q.as_dict(), {'a': 0, 'b': 0, 'c': 0})
 | 
			
		||||
        q += QueryFieldList(fields=["a", "b"], value=QueryFieldList.EXCLUDE)
 | 
			
		||||
        self.assertEqual(q.as_dict(), {"a": 0, "b": 0})
 | 
			
		||||
        q += QueryFieldList(fields=["b", "c"], value=QueryFieldList.EXCLUDE)
 | 
			
		||||
        self.assertEqual(q.as_dict(), {"a": 0, "b": 0, "c": 0})
 | 
			
		||||
 | 
			
		||||
    def test_exclude_include(self):
 | 
			
		||||
        q = QueryFieldList()
 | 
			
		||||
        q += QueryFieldList(fields=['a', 'b'], value=QueryFieldList.EXCLUDE)
 | 
			
		||||
        self.assertEqual(q.as_dict(), {'a': 0, 'b': 0})
 | 
			
		||||
        q += QueryFieldList(fields=['b', 'c'], value=QueryFieldList.ONLY)
 | 
			
		||||
        self.assertEqual(q.as_dict(), {'c': 1})
 | 
			
		||||
        q += QueryFieldList(fields=["a", "b"], value=QueryFieldList.EXCLUDE)
 | 
			
		||||
        self.assertEqual(q.as_dict(), {"a": 0, "b": 0})
 | 
			
		||||
        q += QueryFieldList(fields=["b", "c"], value=QueryFieldList.ONLY)
 | 
			
		||||
        self.assertEqual(q.as_dict(), {"c": 1})
 | 
			
		||||
 | 
			
		||||
    def test_always_include(self):
 | 
			
		||||
        q = QueryFieldList(always_include=['x', 'y'])
 | 
			
		||||
        q += QueryFieldList(fields=['a', 'b', 'x'], value=QueryFieldList.EXCLUDE)
 | 
			
		||||
        q += QueryFieldList(fields=['b', 'c'], value=QueryFieldList.ONLY)
 | 
			
		||||
        self.assertEqual(q.as_dict(), {'x': 1, 'y': 1, 'c': 1})
 | 
			
		||||
        q = QueryFieldList(always_include=["x", "y"])
 | 
			
		||||
        q += QueryFieldList(fields=["a", "b", "x"], value=QueryFieldList.EXCLUDE)
 | 
			
		||||
        q += QueryFieldList(fields=["b", "c"], value=QueryFieldList.ONLY)
 | 
			
		||||
        self.assertEqual(q.as_dict(), {"x": 1, "y": 1, "c": 1})
 | 
			
		||||
 | 
			
		||||
    def test_reset(self):
 | 
			
		||||
        q = QueryFieldList(always_include=['x', 'y'])
 | 
			
		||||
        q += QueryFieldList(fields=['a', 'b', 'x'], value=QueryFieldList.EXCLUDE)
 | 
			
		||||
        q += QueryFieldList(fields=['b', 'c'], value=QueryFieldList.ONLY)
 | 
			
		||||
        self.assertEqual(q.as_dict(), {'x': 1, 'y': 1, 'c': 1})
 | 
			
		||||
        q = QueryFieldList(always_include=["x", "y"])
 | 
			
		||||
        q += QueryFieldList(fields=["a", "b", "x"], value=QueryFieldList.EXCLUDE)
 | 
			
		||||
        q += QueryFieldList(fields=["b", "c"], value=QueryFieldList.ONLY)
 | 
			
		||||
        self.assertEqual(q.as_dict(), {"x": 1, "y": 1, "c": 1})
 | 
			
		||||
        q.reset()
 | 
			
		||||
        self.assertFalse(q)
 | 
			
		||||
        q += QueryFieldList(fields=['b', 'c'], value=QueryFieldList.ONLY)
 | 
			
		||||
        self.assertEqual(q.as_dict(), {'x': 1, 'y': 1, 'b': 1, 'c': 1})
 | 
			
		||||
        q += QueryFieldList(fields=["b", "c"], value=QueryFieldList.ONLY)
 | 
			
		||||
        self.assertEqual(q.as_dict(), {"x": 1, "y": 1, "b": 1, "c": 1})
 | 
			
		||||
 | 
			
		||||
    def test_using_a_slice(self):
 | 
			
		||||
        q = QueryFieldList()
 | 
			
		||||
        q += QueryFieldList(fields=['a'], value={"$slice": 5})
 | 
			
		||||
        self.assertEqual(q.as_dict(), {'a': {"$slice": 5}})
 | 
			
		||||
        q += QueryFieldList(fields=["a"], value={"$slice": 5})
 | 
			
		||||
        self.assertEqual(q.as_dict(), {"a": {"$slice": 5}})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class OnlyExcludeAllTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        connect(db='mongoenginetest')
 | 
			
		||||
        connect(db="mongoenginetest")
 | 
			
		||||
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            age = IntField()
 | 
			
		||||
            meta = {'allow_inheritance': True}
 | 
			
		||||
            meta = {"allow_inheritance": True}
 | 
			
		||||
 | 
			
		||||
        Person.drop_collection()
 | 
			
		||||
        self.Person = Person
 | 
			
		||||
 | 
			
		||||
    def test_mixing_only_exclude(self):
 | 
			
		||||
 | 
			
		||||
        class MyDoc(Document):
 | 
			
		||||
            a = StringField()
 | 
			
		||||
            b = StringField()
 | 
			
		||||
@@ -88,32 +87,32 @@ class OnlyExcludeAllTest(unittest.TestCase):
 | 
			
		||||
            e = StringField()
 | 
			
		||||
            f = StringField()
 | 
			
		||||
 | 
			
		||||
        include = ['a', 'b', 'c', 'd', 'e']
 | 
			
		||||
        exclude = ['d', 'e']
 | 
			
		||||
        only = ['b', 'c']
 | 
			
		||||
        include = ["a", "b", "c", "d", "e"]
 | 
			
		||||
        exclude = ["d", "e"]
 | 
			
		||||
        only = ["b", "c"]
 | 
			
		||||
 | 
			
		||||
        qs = MyDoc.objects.fields(**{i: 1 for i in include})
 | 
			
		||||
        self.assertEqual(qs._loaded_fields.as_dict(),
 | 
			
		||||
                         {'a': 1, 'b': 1, 'c': 1, 'd': 1, 'e': 1})
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            qs._loaded_fields.as_dict(), {"a": 1, "b": 1, "c": 1, "d": 1, "e": 1}
 | 
			
		||||
        )
 | 
			
		||||
        qs = qs.only(*only)
 | 
			
		||||
        self.assertEqual(qs._loaded_fields.as_dict(), {'b': 1, 'c': 1})
 | 
			
		||||
        self.assertEqual(qs._loaded_fields.as_dict(), {"b": 1, "c": 1})
 | 
			
		||||
        qs = qs.exclude(*exclude)
 | 
			
		||||
        self.assertEqual(qs._loaded_fields.as_dict(), {'b': 1, 'c': 1})
 | 
			
		||||
        self.assertEqual(qs._loaded_fields.as_dict(), {"b": 1, "c": 1})
 | 
			
		||||
 | 
			
		||||
        qs = MyDoc.objects.fields(**{i: 1 for i in include})
 | 
			
		||||
        qs = qs.exclude(*exclude)
 | 
			
		||||
        self.assertEqual(qs._loaded_fields.as_dict(), {'a': 1, 'b': 1, 'c': 1})
 | 
			
		||||
        self.assertEqual(qs._loaded_fields.as_dict(), {"a": 1, "b": 1, "c": 1})
 | 
			
		||||
        qs = qs.only(*only)
 | 
			
		||||
        self.assertEqual(qs._loaded_fields.as_dict(), {'b': 1, 'c': 1})
 | 
			
		||||
        self.assertEqual(qs._loaded_fields.as_dict(), {"b": 1, "c": 1})
 | 
			
		||||
 | 
			
		||||
        qs = MyDoc.objects.exclude(*exclude)
 | 
			
		||||
        qs = qs.fields(**{i: 1 for i in include})
 | 
			
		||||
        self.assertEqual(qs._loaded_fields.as_dict(), {'a': 1, 'b': 1, 'c': 1})
 | 
			
		||||
        self.assertEqual(qs._loaded_fields.as_dict(), {"a": 1, "b": 1, "c": 1})
 | 
			
		||||
        qs = qs.only(*only)
 | 
			
		||||
        self.assertEqual(qs._loaded_fields.as_dict(), {'b': 1, 'c': 1})
 | 
			
		||||
        self.assertEqual(qs._loaded_fields.as_dict(), {"b": 1, "c": 1})
 | 
			
		||||
 | 
			
		||||
    def test_slicing(self):
 | 
			
		||||
 | 
			
		||||
        class MyDoc(Document):
 | 
			
		||||
            a = ListField()
 | 
			
		||||
            b = ListField()
 | 
			
		||||
@@ -122,24 +121,23 @@ class OnlyExcludeAllTest(unittest.TestCase):
 | 
			
		||||
            e = ListField()
 | 
			
		||||
            f = ListField()
 | 
			
		||||
 | 
			
		||||
        include = ['a', 'b', 'c', 'd', 'e']
 | 
			
		||||
        exclude = ['d', 'e']
 | 
			
		||||
        only = ['b', 'c']
 | 
			
		||||
        include = ["a", "b", "c", "d", "e"]
 | 
			
		||||
        exclude = ["d", "e"]
 | 
			
		||||
        only = ["b", "c"]
 | 
			
		||||
 | 
			
		||||
        qs = MyDoc.objects.fields(**{i: 1 for i in include})
 | 
			
		||||
        qs = qs.exclude(*exclude)
 | 
			
		||||
        qs = qs.only(*only)
 | 
			
		||||
        qs = qs.fields(slice__b=5)
 | 
			
		||||
        self.assertEqual(qs._loaded_fields.as_dict(),
 | 
			
		||||
                         {'b': {'$slice': 5}, 'c': 1})
 | 
			
		||||
        self.assertEqual(qs._loaded_fields.as_dict(), {"b": {"$slice": 5}, "c": 1})
 | 
			
		||||
 | 
			
		||||
        qs = qs.fields(slice__c=[5, 1])
 | 
			
		||||
        self.assertEqual(qs._loaded_fields.as_dict(),
 | 
			
		||||
                         {'b': {'$slice': 5}, 'c': {'$slice': [5, 1]}})
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            qs._loaded_fields.as_dict(), {"b": {"$slice": 5}, "c": {"$slice": [5, 1]}}
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        qs = qs.exclude('c')
 | 
			
		||||
        self.assertEqual(qs._loaded_fields.as_dict(),
 | 
			
		||||
                         {'b': {'$slice': 5}})
 | 
			
		||||
        qs = qs.exclude("c")
 | 
			
		||||
        self.assertEqual(qs._loaded_fields.as_dict(), {"b": {"$slice": 5}})
 | 
			
		||||
 | 
			
		||||
    def test_mix_slice_with_other_fields(self):
 | 
			
		||||
        class MyDoc(Document):
 | 
			
		||||
@@ -148,43 +146,42 @@ class OnlyExcludeAllTest(unittest.TestCase):
 | 
			
		||||
            c = ListField()
 | 
			
		||||
 | 
			
		||||
        qs = MyDoc.objects.fields(a=1, b=0, slice__c=2)
 | 
			
		||||
        self.assertEqual(qs._loaded_fields.as_dict(),
 | 
			
		||||
                         {'c': {'$slice': 2}, 'a': 1})
 | 
			
		||||
        self.assertEqual(qs._loaded_fields.as_dict(), {"c": {"$slice": 2}, "a": 1})
 | 
			
		||||
 | 
			
		||||
    def test_only(self):
 | 
			
		||||
        """Ensure that QuerySet.only only returns the requested fields.
 | 
			
		||||
        """
 | 
			
		||||
        person = self.Person(name='test', age=25)
 | 
			
		||||
        person = self.Person(name="test", age=25)
 | 
			
		||||
        person.save()
 | 
			
		||||
 | 
			
		||||
        obj = self.Person.objects.only('name').get()
 | 
			
		||||
        obj = self.Person.objects.only("name").get()
 | 
			
		||||
        self.assertEqual(obj.name, person.name)
 | 
			
		||||
        self.assertEqual(obj.age, None)
 | 
			
		||||
 | 
			
		||||
        obj = self.Person.objects.only('age').get()
 | 
			
		||||
        obj = self.Person.objects.only("age").get()
 | 
			
		||||
        self.assertEqual(obj.name, None)
 | 
			
		||||
        self.assertEqual(obj.age, person.age)
 | 
			
		||||
 | 
			
		||||
        obj = self.Person.objects.only('name', 'age').get()
 | 
			
		||||
        obj = self.Person.objects.only("name", "age").get()
 | 
			
		||||
        self.assertEqual(obj.name, person.name)
 | 
			
		||||
        self.assertEqual(obj.age, person.age)
 | 
			
		||||
 | 
			
		||||
        obj = self.Person.objects.only(*('id', 'name',)).get()
 | 
			
		||||
        obj = self.Person.objects.only(*("id", "name")).get()
 | 
			
		||||
        self.assertEqual(obj.name, person.name)
 | 
			
		||||
        self.assertEqual(obj.age, None)
 | 
			
		||||
 | 
			
		||||
        # Check polymorphism still works
 | 
			
		||||
        class Employee(self.Person):
 | 
			
		||||
            salary = IntField(db_field='wage')
 | 
			
		||||
            salary = IntField(db_field="wage")
 | 
			
		||||
 | 
			
		||||
        employee = Employee(name='test employee', age=40, salary=30000)
 | 
			
		||||
        employee = Employee(name="test employee", age=40, salary=30000)
 | 
			
		||||
        employee.save()
 | 
			
		||||
 | 
			
		||||
        obj = self.Person.objects(id=employee.id).only('age').get()
 | 
			
		||||
        obj = self.Person.objects(id=employee.id).only("age").get()
 | 
			
		||||
        self.assertIsInstance(obj, Employee)
 | 
			
		||||
 | 
			
		||||
        # Check field names are looked up properly
 | 
			
		||||
        obj = Employee.objects(id=employee.id).only('salary').get()
 | 
			
		||||
        obj = Employee.objects(id=employee.id).only("salary").get()
 | 
			
		||||
        self.assertEqual(obj.salary, employee.salary)
 | 
			
		||||
        self.assertEqual(obj.name, None)
 | 
			
		||||
 | 
			
		||||
@@ -208,35 +205,41 @@ class OnlyExcludeAllTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
        BlogPost.drop_collection()
 | 
			
		||||
 | 
			
		||||
        post = BlogPost(content='Had a good coffee today...', various={'test_dynamic': {'some': True}})
 | 
			
		||||
        post.author = User(name='Test User')
 | 
			
		||||
        post.comments = [Comment(title='I aggree', text='Great post!'), Comment(title='Coffee', text='I hate coffee')]
 | 
			
		||||
        post = BlogPost(
 | 
			
		||||
            content="Had a good coffee today...",
 | 
			
		||||
            various={"test_dynamic": {"some": True}},
 | 
			
		||||
        )
 | 
			
		||||
        post.author = User(name="Test User")
 | 
			
		||||
        post.comments = [
 | 
			
		||||
            Comment(title="I aggree", text="Great post!"),
 | 
			
		||||
            Comment(title="Coffee", text="I hate coffee"),
 | 
			
		||||
        ]
 | 
			
		||||
        post.save()
 | 
			
		||||
 | 
			
		||||
        obj = BlogPost.objects.only('author.name',).get()
 | 
			
		||||
        obj = BlogPost.objects.only("author.name").get()
 | 
			
		||||
        self.assertEqual(obj.content, None)
 | 
			
		||||
        self.assertEqual(obj.author.email, None)
 | 
			
		||||
        self.assertEqual(obj.author.name, 'Test User')
 | 
			
		||||
        self.assertEqual(obj.author.name, "Test User")
 | 
			
		||||
        self.assertEqual(obj.comments, [])
 | 
			
		||||
 | 
			
		||||
        obj = BlogPost.objects.only('various.test_dynamic.some').get()
 | 
			
		||||
        obj = BlogPost.objects.only("various.test_dynamic.some").get()
 | 
			
		||||
        self.assertEqual(obj.various["test_dynamic"].some, True)
 | 
			
		||||
 | 
			
		||||
        obj = BlogPost.objects.only('content', 'comments.title',).get()
 | 
			
		||||
        self.assertEqual(obj.content, 'Had a good coffee today...')
 | 
			
		||||
        obj = BlogPost.objects.only("content", "comments.title").get()
 | 
			
		||||
        self.assertEqual(obj.content, "Had a good coffee today...")
 | 
			
		||||
        self.assertEqual(obj.author, None)
 | 
			
		||||
        self.assertEqual(obj.comments[0].title, 'I aggree')
 | 
			
		||||
        self.assertEqual(obj.comments[1].title, 'Coffee')
 | 
			
		||||
        self.assertEqual(obj.comments[0].title, "I aggree")
 | 
			
		||||
        self.assertEqual(obj.comments[1].title, "Coffee")
 | 
			
		||||
        self.assertEqual(obj.comments[0].text, None)
 | 
			
		||||
        self.assertEqual(obj.comments[1].text, None)
 | 
			
		||||
 | 
			
		||||
        obj = BlogPost.objects.only('comments',).get()
 | 
			
		||||
        obj = BlogPost.objects.only("comments").get()
 | 
			
		||||
        self.assertEqual(obj.content, None)
 | 
			
		||||
        self.assertEqual(obj.author, None)
 | 
			
		||||
        self.assertEqual(obj.comments[0].title, 'I aggree')
 | 
			
		||||
        self.assertEqual(obj.comments[1].title, 'Coffee')
 | 
			
		||||
        self.assertEqual(obj.comments[0].text, 'Great post!')
 | 
			
		||||
        self.assertEqual(obj.comments[1].text, 'I hate coffee')
 | 
			
		||||
        self.assertEqual(obj.comments[0].title, "I aggree")
 | 
			
		||||
        self.assertEqual(obj.comments[1].title, "Coffee")
 | 
			
		||||
        self.assertEqual(obj.comments[0].text, "Great post!")
 | 
			
		||||
        self.assertEqual(obj.comments[1].text, "I hate coffee")
 | 
			
		||||
 | 
			
		||||
        BlogPost.drop_collection()
 | 
			
		||||
 | 
			
		||||
@@ -256,15 +259,18 @@ class OnlyExcludeAllTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
        BlogPost.drop_collection()
 | 
			
		||||
 | 
			
		||||
        post = BlogPost(content='Had a good coffee today...')
 | 
			
		||||
        post.author = User(name='Test User')
 | 
			
		||||
        post.comments = [Comment(title='I aggree', text='Great post!'), Comment(title='Coffee', text='I hate coffee')]
 | 
			
		||||
        post = BlogPost(content="Had a good coffee today...")
 | 
			
		||||
        post.author = User(name="Test User")
 | 
			
		||||
        post.comments = [
 | 
			
		||||
            Comment(title="I aggree", text="Great post!"),
 | 
			
		||||
            Comment(title="Coffee", text="I hate coffee"),
 | 
			
		||||
        ]
 | 
			
		||||
        post.save()
 | 
			
		||||
 | 
			
		||||
        obj = BlogPost.objects.exclude('author', 'comments.text').get()
 | 
			
		||||
        obj = BlogPost.objects.exclude("author", "comments.text").get()
 | 
			
		||||
        self.assertEqual(obj.author, None)
 | 
			
		||||
        self.assertEqual(obj.content, 'Had a good coffee today...')
 | 
			
		||||
        self.assertEqual(obj.comments[0].title, 'I aggree')
 | 
			
		||||
        self.assertEqual(obj.content, "Had a good coffee today...")
 | 
			
		||||
        self.assertEqual(obj.comments[0].title, "I aggree")
 | 
			
		||||
        self.assertEqual(obj.comments[0].text, None)
 | 
			
		||||
 | 
			
		||||
        BlogPost.drop_collection()
 | 
			
		||||
@@ -283,32 +289,43 @@ class OnlyExcludeAllTest(unittest.TestCase):
 | 
			
		||||
            attachments = ListField(EmbeddedDocumentField(Attachment))
 | 
			
		||||
 | 
			
		||||
        Email.drop_collection()
 | 
			
		||||
        email = Email(sender='me', to='you', subject='From Russia with Love', body='Hello!', content_type='text/plain')
 | 
			
		||||
        email = Email(
 | 
			
		||||
            sender="me",
 | 
			
		||||
            to="you",
 | 
			
		||||
            subject="From Russia with Love",
 | 
			
		||||
            body="Hello!",
 | 
			
		||||
            content_type="text/plain",
 | 
			
		||||
        )
 | 
			
		||||
        email.attachments = [
 | 
			
		||||
            Attachment(name='file1.doc', content='ABC'),
 | 
			
		||||
            Attachment(name='file2.doc', content='XYZ'),
 | 
			
		||||
            Attachment(name="file1.doc", content="ABC"),
 | 
			
		||||
            Attachment(name="file2.doc", content="XYZ"),
 | 
			
		||||
        ]
 | 
			
		||||
        email.save()
 | 
			
		||||
 | 
			
		||||
        obj = Email.objects.exclude('content_type').exclude('body').get()
 | 
			
		||||
        self.assertEqual(obj.sender, 'me')
 | 
			
		||||
        self.assertEqual(obj.to, 'you')
 | 
			
		||||
        self.assertEqual(obj.subject, 'From Russia with Love')
 | 
			
		||||
        obj = Email.objects.exclude("content_type").exclude("body").get()
 | 
			
		||||
        self.assertEqual(obj.sender, "me")
 | 
			
		||||
        self.assertEqual(obj.to, "you")
 | 
			
		||||
        self.assertEqual(obj.subject, "From Russia with Love")
 | 
			
		||||
        self.assertEqual(obj.body, None)
 | 
			
		||||
        self.assertEqual(obj.content_type, None)
 | 
			
		||||
 | 
			
		||||
        obj = Email.objects.only('sender', 'to').exclude('body', 'sender').get()
 | 
			
		||||
        obj = Email.objects.only("sender", "to").exclude("body", "sender").get()
 | 
			
		||||
        self.assertEqual(obj.sender, None)
 | 
			
		||||
        self.assertEqual(obj.to, 'you')
 | 
			
		||||
        self.assertEqual(obj.to, "you")
 | 
			
		||||
        self.assertEqual(obj.subject, None)
 | 
			
		||||
        self.assertEqual(obj.body, None)
 | 
			
		||||
        self.assertEqual(obj.content_type, None)
 | 
			
		||||
 | 
			
		||||
        obj = Email.objects.exclude('attachments.content').exclude('body').only('to', 'attachments.name').get()
 | 
			
		||||
        self.assertEqual(obj.attachments[0].name, 'file1.doc')
 | 
			
		||||
        obj = (
 | 
			
		||||
            Email.objects.exclude("attachments.content")
 | 
			
		||||
            .exclude("body")
 | 
			
		||||
            .only("to", "attachments.name")
 | 
			
		||||
            .get()
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(obj.attachments[0].name, "file1.doc")
 | 
			
		||||
        self.assertEqual(obj.attachments[0].content, None)
 | 
			
		||||
        self.assertEqual(obj.sender, None)
 | 
			
		||||
        self.assertEqual(obj.to, 'you')
 | 
			
		||||
        self.assertEqual(obj.to, "you")
 | 
			
		||||
        self.assertEqual(obj.subject, None)
 | 
			
		||||
        self.assertEqual(obj.body, None)
 | 
			
		||||
        self.assertEqual(obj.content_type, None)
 | 
			
		||||
@@ -316,7 +333,6 @@ class OnlyExcludeAllTest(unittest.TestCase):
 | 
			
		||||
        Email.drop_collection()
 | 
			
		||||
 | 
			
		||||
    def test_all_fields(self):
 | 
			
		||||
 | 
			
		||||
        class Email(Document):
 | 
			
		||||
            sender = StringField()
 | 
			
		||||
            to = StringField()
 | 
			
		||||
@@ -326,21 +342,33 @@ class OnlyExcludeAllTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
        Email.drop_collection()
 | 
			
		||||
 | 
			
		||||
        email = Email(sender='me', to='you', subject='From Russia with Love', body='Hello!', content_type='text/plain')
 | 
			
		||||
        email = Email(
 | 
			
		||||
            sender="me",
 | 
			
		||||
            to="you",
 | 
			
		||||
            subject="From Russia with Love",
 | 
			
		||||
            body="Hello!",
 | 
			
		||||
            content_type="text/plain",
 | 
			
		||||
        )
 | 
			
		||||
        email.save()
 | 
			
		||||
 | 
			
		||||
        obj = Email.objects.exclude('content_type', 'body').only('to', 'body').all_fields().get()
 | 
			
		||||
        self.assertEqual(obj.sender, 'me')
 | 
			
		||||
        self.assertEqual(obj.to, 'you')
 | 
			
		||||
        self.assertEqual(obj.subject, 'From Russia with Love')
 | 
			
		||||
        self.assertEqual(obj.body, 'Hello!')
 | 
			
		||||
        self.assertEqual(obj.content_type, 'text/plain')
 | 
			
		||||
        obj = (
 | 
			
		||||
            Email.objects.exclude("content_type", "body")
 | 
			
		||||
            .only("to", "body")
 | 
			
		||||
            .all_fields()
 | 
			
		||||
            .get()
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(obj.sender, "me")
 | 
			
		||||
        self.assertEqual(obj.to, "you")
 | 
			
		||||
        self.assertEqual(obj.subject, "From Russia with Love")
 | 
			
		||||
        self.assertEqual(obj.body, "Hello!")
 | 
			
		||||
        self.assertEqual(obj.content_type, "text/plain")
 | 
			
		||||
 | 
			
		||||
        Email.drop_collection()
 | 
			
		||||
 | 
			
		||||
    def test_slicing_fields(self):
 | 
			
		||||
        """Ensure that query slicing an array works.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Numbers(Document):
 | 
			
		||||
            n = ListField(IntField())
 | 
			
		||||
 | 
			
		||||
@@ -414,11 +442,10 @@ class OnlyExcludeAllTest(unittest.TestCase):
 | 
			
		||||
        self.assertEqual(numbers.embedded.n, [-5, -4, -3, -2, -1])
 | 
			
		||||
 | 
			
		||||
    def test_exclude_from_subclasses_docs(self):
 | 
			
		||||
 | 
			
		||||
        class Base(Document):
 | 
			
		||||
            username = StringField()
 | 
			
		||||
 | 
			
		||||
            meta = {'allow_inheritance': True}
 | 
			
		||||
            meta = {"allow_inheritance": True}
 | 
			
		||||
 | 
			
		||||
        class Anon(Base):
 | 
			
		||||
            anon = BooleanField()
 | 
			
		||||
@@ -436,5 +463,5 @@ class OnlyExcludeAllTest(unittest.TestCase):
 | 
			
		||||
        self.assertRaises(LookUpError, Base.objects.exclude, "made_up")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    unittest.main()
 | 
			
		||||
 
 | 
			
		||||
@@ -10,9 +10,9 @@ __all__ = ("GeoQueriesTest",)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GeoQueriesTest(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
    def _create_event_data(self, point_field_class=GeoPointField):
 | 
			
		||||
        """Create some sample data re-used in many of the tests below."""
 | 
			
		||||
 | 
			
		||||
        class Event(Document):
 | 
			
		||||
            title = StringField()
 | 
			
		||||
            date = DateTimeField()
 | 
			
		||||
@@ -28,15 +28,18 @@ class GeoQueriesTest(MongoDBTestCase):
 | 
			
		||||
        event1 = Event.objects.create(
 | 
			
		||||
            title="Coltrane Motion @ Double Door",
 | 
			
		||||
            date=datetime.datetime.now() - datetime.timedelta(days=1),
 | 
			
		||||
            location=[-87.677137, 41.909889])
 | 
			
		||||
            location=[-87.677137, 41.909889],
 | 
			
		||||
        )
 | 
			
		||||
        event2 = Event.objects.create(
 | 
			
		||||
            title="Coltrane Motion @ Bottom of the Hill",
 | 
			
		||||
            date=datetime.datetime.now() - datetime.timedelta(days=10),
 | 
			
		||||
            location=[-122.4194155, 37.7749295])
 | 
			
		||||
            location=[-122.4194155, 37.7749295],
 | 
			
		||||
        )
 | 
			
		||||
        event3 = Event.objects.create(
 | 
			
		||||
            title="Coltrane Motion @ Empty Bottle",
 | 
			
		||||
            date=datetime.datetime.now(),
 | 
			
		||||
            location=[-87.686638, 41.900474])
 | 
			
		||||
            location=[-87.686638, 41.900474],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        return event1, event2, event3
 | 
			
		||||
 | 
			
		||||
@@ -65,8 +68,7 @@ class GeoQueriesTest(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        # find events within 10 degrees of san francisco
 | 
			
		||||
        point = [-122.415579, 37.7566023]
 | 
			
		||||
        events = self.Event.objects(location__near=point,
 | 
			
		||||
                                    location__max_distance=10)
 | 
			
		||||
        events = self.Event.objects(location__near=point, location__max_distance=10)
 | 
			
		||||
        self.assertEqual(events.count(), 1)
 | 
			
		||||
        self.assertEqual(events[0], event2)
 | 
			
		||||
 | 
			
		||||
@@ -78,8 +80,7 @@ class GeoQueriesTest(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        # find events at least 10 degrees away of san francisco
 | 
			
		||||
        point = [-122.415579, 37.7566023]
 | 
			
		||||
        events = self.Event.objects(location__near=point,
 | 
			
		||||
                                    location__min_distance=10)
 | 
			
		||||
        events = self.Event.objects(location__near=point, location__min_distance=10)
 | 
			
		||||
        self.assertEqual(events.count(), 2)
 | 
			
		||||
 | 
			
		||||
    def test_within_distance(self):
 | 
			
		||||
@@ -88,8 +89,7 @@ class GeoQueriesTest(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        # find events within 5 degrees of pitchfork office, chicago
 | 
			
		||||
        point_and_distance = [[-87.67892, 41.9120459], 5]
 | 
			
		||||
        events = self.Event.objects(
 | 
			
		||||
            location__within_distance=point_and_distance)
 | 
			
		||||
        events = self.Event.objects(location__within_distance=point_and_distance)
 | 
			
		||||
        self.assertEqual(events.count(), 2)
 | 
			
		||||
        events = list(events)
 | 
			
		||||
        self.assertNotIn(event2, events)
 | 
			
		||||
@@ -98,21 +98,18 @@ class GeoQueriesTest(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        # find events within 10 degrees of san francisco
 | 
			
		||||
        point_and_distance = [[-122.415579, 37.7566023], 10]
 | 
			
		||||
        events = self.Event.objects(
 | 
			
		||||
            location__within_distance=point_and_distance)
 | 
			
		||||
        events = self.Event.objects(location__within_distance=point_and_distance)
 | 
			
		||||
        self.assertEqual(events.count(), 1)
 | 
			
		||||
        self.assertEqual(events[0], event2)
 | 
			
		||||
 | 
			
		||||
        # find events within 1 degree of greenpoint, broolyn, nyc, ny
 | 
			
		||||
        point_and_distance = [[-73.9509714, 40.7237134], 1]
 | 
			
		||||
        events = self.Event.objects(
 | 
			
		||||
            location__within_distance=point_and_distance)
 | 
			
		||||
        events = self.Event.objects(location__within_distance=point_and_distance)
 | 
			
		||||
        self.assertEqual(events.count(), 0)
 | 
			
		||||
 | 
			
		||||
        # ensure ordering is respected by "within_distance"
 | 
			
		||||
        point_and_distance = [[-87.67892, 41.9120459], 10]
 | 
			
		||||
        events = self.Event.objects(
 | 
			
		||||
            location__within_distance=point_and_distance)
 | 
			
		||||
        events = self.Event.objects(location__within_distance=point_and_distance)
 | 
			
		||||
        events = events.order_by("-date")
 | 
			
		||||
        self.assertEqual(events.count(), 2)
 | 
			
		||||
        self.assertEqual(events[0], event3)
 | 
			
		||||
@@ -145,7 +142,7 @@ class GeoQueriesTest(MongoDBTestCase):
 | 
			
		||||
        polygon2 = [
 | 
			
		||||
            (-1.742249, 54.033586),
 | 
			
		||||
            (-1.225891, 52.792797),
 | 
			
		||||
            (-4.40094, 53.389881)
 | 
			
		||||
            (-4.40094, 53.389881),
 | 
			
		||||
        ]
 | 
			
		||||
        events = self.Event.objects(location__within_polygon=polygon2)
 | 
			
		||||
        self.assertEqual(events.count(), 0)
 | 
			
		||||
@@ -154,9 +151,7 @@ class GeoQueriesTest(MongoDBTestCase):
 | 
			
		||||
        """Make sure the "near" operator works with a PointField, which
 | 
			
		||||
        corresponds to a 2dsphere index.
 | 
			
		||||
        """
 | 
			
		||||
        event1, event2, event3 = self._create_event_data(
 | 
			
		||||
            point_field_class=PointField
 | 
			
		||||
        )
 | 
			
		||||
        event1, event2, event3 = self._create_event_data(point_field_class=PointField)
 | 
			
		||||
 | 
			
		||||
        # find all events "near" pitchfork office, chicago.
 | 
			
		||||
        # note that "near" will show the san francisco event, too,
 | 
			
		||||
@@ -175,26 +170,23 @@ class GeoQueriesTest(MongoDBTestCase):
 | 
			
		||||
        """Ensure the "max_distance" operator works alongside the "near"
 | 
			
		||||
        operator with a 2dsphere index.
 | 
			
		||||
        """
 | 
			
		||||
        event1, event2, event3 = self._create_event_data(
 | 
			
		||||
            point_field_class=PointField
 | 
			
		||||
        )
 | 
			
		||||
        event1, event2, event3 = self._create_event_data(point_field_class=PointField)
 | 
			
		||||
 | 
			
		||||
        # find events within 10km of san francisco
 | 
			
		||||
        point = [-122.415579, 37.7566023]
 | 
			
		||||
        events = self.Event.objects(location__near=point,
 | 
			
		||||
                                    location__max_distance=10000)
 | 
			
		||||
        events = self.Event.objects(location__near=point, location__max_distance=10000)
 | 
			
		||||
        self.assertEqual(events.count(), 1)
 | 
			
		||||
        self.assertEqual(events[0], event2)
 | 
			
		||||
 | 
			
		||||
        # find events within 1km of greenpoint, broolyn, nyc, ny
 | 
			
		||||
        events = self.Event.objects(location__near=[-73.9509714, 40.7237134],
 | 
			
		||||
                                    location__max_distance=1000)
 | 
			
		||||
        events = self.Event.objects(
 | 
			
		||||
            location__near=[-73.9509714, 40.7237134], location__max_distance=1000
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(events.count(), 0)
 | 
			
		||||
 | 
			
		||||
        # ensure ordering is respected by "near"
 | 
			
		||||
        events = self.Event.objects(
 | 
			
		||||
            location__near=[-87.67892, 41.9120459],
 | 
			
		||||
            location__max_distance=10000
 | 
			
		||||
            location__near=[-87.67892, 41.9120459], location__max_distance=10000
 | 
			
		||||
        ).order_by("-date")
 | 
			
		||||
        self.assertEqual(events.count(), 2)
 | 
			
		||||
        self.assertEqual(events[0], event3)
 | 
			
		||||
@@ -203,9 +195,7 @@ class GeoQueriesTest(MongoDBTestCase):
 | 
			
		||||
        """Ensure the "geo_within_box" operator works with a 2dsphere
 | 
			
		||||
        index.
 | 
			
		||||
        """
 | 
			
		||||
        event1, event2, event3 = self._create_event_data(
 | 
			
		||||
            point_field_class=PointField
 | 
			
		||||
        )
 | 
			
		||||
        event1, event2, event3 = self._create_event_data(point_field_class=PointField)
 | 
			
		||||
 | 
			
		||||
        # check that within_box works
 | 
			
		||||
        box = [(-125.0, 35.0), (-100.0, 40.0)]
 | 
			
		||||
@@ -217,9 +207,7 @@ class GeoQueriesTest(MongoDBTestCase):
 | 
			
		||||
        """Ensure the "geo_within_polygon" operator works with a
 | 
			
		||||
        2dsphere index.
 | 
			
		||||
        """
 | 
			
		||||
        event1, event2, event3 = self._create_event_data(
 | 
			
		||||
            point_field_class=PointField
 | 
			
		||||
        )
 | 
			
		||||
        event1, event2, event3 = self._create_event_data(point_field_class=PointField)
 | 
			
		||||
 | 
			
		||||
        polygon = [
 | 
			
		||||
            (-87.694445, 41.912114),
 | 
			
		||||
@@ -235,7 +223,7 @@ class GeoQueriesTest(MongoDBTestCase):
 | 
			
		||||
        polygon2 = [
 | 
			
		||||
            (-1.742249, 54.033586),
 | 
			
		||||
            (-1.225891, 52.792797),
 | 
			
		||||
            (-4.40094, 53.389881)
 | 
			
		||||
            (-4.40094, 53.389881),
 | 
			
		||||
        ]
 | 
			
		||||
        events = self.Event.objects(location__geo_within_polygon=polygon2)
 | 
			
		||||
        self.assertEqual(events.count(), 0)
 | 
			
		||||
@@ -244,23 +232,20 @@ class GeoQueriesTest(MongoDBTestCase):
 | 
			
		||||
        """Ensure "min_distace" and "max_distance" operators work well
 | 
			
		||||
        together with the "near" operator in a 2dsphere index.
 | 
			
		||||
        """
 | 
			
		||||
        event1, event2, event3 = self._create_event_data(
 | 
			
		||||
            point_field_class=PointField
 | 
			
		||||
        )
 | 
			
		||||
        event1, event2, event3 = self._create_event_data(point_field_class=PointField)
 | 
			
		||||
 | 
			
		||||
        # ensure min_distance and max_distance combine well
 | 
			
		||||
        events = self.Event.objects(
 | 
			
		||||
            location__near=[-87.67892, 41.9120459],
 | 
			
		||||
            location__min_distance=1000,
 | 
			
		||||
            location__max_distance=10000
 | 
			
		||||
            location__max_distance=10000,
 | 
			
		||||
        ).order_by("-date")
 | 
			
		||||
        self.assertEqual(events.count(), 1)
 | 
			
		||||
        self.assertEqual(events[0], event3)
 | 
			
		||||
 | 
			
		||||
        # ensure ordering is respected by "near" with "min_distance"
 | 
			
		||||
        events = self.Event.objects(
 | 
			
		||||
            location__near=[-87.67892, 41.9120459],
 | 
			
		||||
            location__min_distance=10000
 | 
			
		||||
            location__near=[-87.67892, 41.9120459], location__min_distance=10000
 | 
			
		||||
        ).order_by("-date")
 | 
			
		||||
        self.assertEqual(events.count(), 1)
 | 
			
		||||
        self.assertEqual(events[0], event2)
 | 
			
		||||
@@ -269,14 +254,11 @@ class GeoQueriesTest(MongoDBTestCase):
 | 
			
		||||
        """Make sure the "geo_within_center" operator works with a
 | 
			
		||||
        2dsphere index.
 | 
			
		||||
        """
 | 
			
		||||
        event1, event2, event3 = self._create_event_data(
 | 
			
		||||
            point_field_class=PointField
 | 
			
		||||
        )
 | 
			
		||||
        event1, event2, event3 = self._create_event_data(point_field_class=PointField)
 | 
			
		||||
 | 
			
		||||
        # find events within 5 degrees of pitchfork office, chicago
 | 
			
		||||
        point_and_distance = [[-87.67892, 41.9120459], 2]
 | 
			
		||||
        events = self.Event.objects(
 | 
			
		||||
            location__geo_within_center=point_and_distance)
 | 
			
		||||
        events = self.Event.objects(location__geo_within_center=point_and_distance)
 | 
			
		||||
        self.assertEqual(events.count(), 2)
 | 
			
		||||
        events = list(events)
 | 
			
		||||
        self.assertNotIn(event2, events)
 | 
			
		||||
@@ -287,6 +269,7 @@ class GeoQueriesTest(MongoDBTestCase):
 | 
			
		||||
        """Helper test method ensuring given point field class works
 | 
			
		||||
        well in an embedded document.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Venue(EmbeddedDocument):
 | 
			
		||||
            location = point_field_class()
 | 
			
		||||
            name = StringField()
 | 
			
		||||
@@ -300,12 +283,11 @@ class GeoQueriesTest(MongoDBTestCase):
 | 
			
		||||
        venue1 = Venue(name="The Rock", location=[-87.677137, 41.909889])
 | 
			
		||||
        venue2 = Venue(name="The Bridge", location=[-122.4194155, 37.7749295])
 | 
			
		||||
 | 
			
		||||
        event1 = Event(title="Coltrane Motion @ Double Door",
 | 
			
		||||
                       venue=venue1).save()
 | 
			
		||||
        event2 = Event(title="Coltrane Motion @ Bottom of the Hill",
 | 
			
		||||
                       venue=venue2).save()
 | 
			
		||||
        event3 = Event(title="Coltrane Motion @ Empty Bottle",
 | 
			
		||||
                       venue=venue1).save()
 | 
			
		||||
        event1 = Event(title="Coltrane Motion @ Double Door", venue=venue1).save()
 | 
			
		||||
        event2 = Event(
 | 
			
		||||
            title="Coltrane Motion @ Bottom of the Hill", venue=venue2
 | 
			
		||||
        ).save()
 | 
			
		||||
        event3 = Event(title="Coltrane Motion @ Empty Bottle", venue=venue1).save()
 | 
			
		||||
 | 
			
		||||
        # find all events "near" pitchfork office, chicago.
 | 
			
		||||
        # note that "near" will show the san francisco event, too,
 | 
			
		||||
@@ -324,6 +306,7 @@ class GeoQueriesTest(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_spherical_geospatial_operators(self):
 | 
			
		||||
        """Ensure that spherical geospatial queries are working."""
 | 
			
		||||
 | 
			
		||||
        class Point(Document):
 | 
			
		||||
            location = GeoPointField()
 | 
			
		||||
 | 
			
		||||
@@ -343,26 +326,26 @@ class GeoQueriesTest(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        # Same behavior for _within_spherical_distance
 | 
			
		||||
        points = Point.objects(
 | 
			
		||||
            location__within_spherical_distance=[
 | 
			
		||||
                [-122, 37.5],
 | 
			
		||||
                60 / earth_radius
 | 
			
		||||
            ]
 | 
			
		||||
            location__within_spherical_distance=[[-122, 37.5], 60 / earth_radius]
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(points.count(), 2)
 | 
			
		||||
 | 
			
		||||
        points = Point.objects(location__near_sphere=[-122, 37.5],
 | 
			
		||||
                               location__max_distance=60 / earth_radius)
 | 
			
		||||
        points = Point.objects(
 | 
			
		||||
            location__near_sphere=[-122, 37.5], location__max_distance=60 / earth_radius
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(points.count(), 2)
 | 
			
		||||
 | 
			
		||||
        # Test query works with max_distance, being farer from one point
 | 
			
		||||
        points = Point.objects(location__near_sphere=[-122, 37.8],
 | 
			
		||||
                               location__max_distance=60 / earth_radius)
 | 
			
		||||
        points = Point.objects(
 | 
			
		||||
            location__near_sphere=[-122, 37.8], location__max_distance=60 / earth_radius
 | 
			
		||||
        )
 | 
			
		||||
        close_point = points.first()
 | 
			
		||||
        self.assertEqual(points.count(), 1)
 | 
			
		||||
 | 
			
		||||
        # Test query works with min_distance, being farer from one point
 | 
			
		||||
        points = Point.objects(location__near_sphere=[-122, 37.8],
 | 
			
		||||
                               location__min_distance=60 / earth_radius)
 | 
			
		||||
        points = Point.objects(
 | 
			
		||||
            location__near_sphere=[-122, 37.8], location__min_distance=60 / earth_radius
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(points.count(), 1)
 | 
			
		||||
        far_point = points.first()
 | 
			
		||||
        self.assertNotEqual(close_point, far_point)
 | 
			
		||||
@@ -384,10 +367,7 @@ class GeoQueriesTest(MongoDBTestCase):
 | 
			
		||||
        # Finds only one point because only the first point is within 60km of
 | 
			
		||||
        # the reference point to the south.
 | 
			
		||||
        points = Point.objects(
 | 
			
		||||
            location__within_spherical_distance=[
 | 
			
		||||
                [-122, 36.5],
 | 
			
		||||
                60 / earth_radius
 | 
			
		||||
            ]
 | 
			
		||||
            location__within_spherical_distance=[[-122, 36.5], 60 / earth_radius]
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(points.count(), 1)
 | 
			
		||||
        self.assertEqual(points[0].id, south_point.id)
 | 
			
		||||
@@ -413,8 +393,10 @@ class GeoQueriesTest(MongoDBTestCase):
 | 
			
		||||
        self.assertEqual(1, roads)
 | 
			
		||||
 | 
			
		||||
        # Within
 | 
			
		||||
        polygon = {"type": "Polygon",
 | 
			
		||||
                   "coordinates": [[[40, 5], [40, 6], [41, 6], [41, 5], [40, 5]]]}
 | 
			
		||||
        polygon = {
 | 
			
		||||
            "type": "Polygon",
 | 
			
		||||
            "coordinates": [[[40, 5], [40, 6], [41, 6], [41, 5], [40, 5]]],
 | 
			
		||||
        }
 | 
			
		||||
        roads = Road.objects.filter(line__geo_within=polygon["coordinates"]).count()
 | 
			
		||||
        self.assertEqual(1, roads)
 | 
			
		||||
 | 
			
		||||
@@ -425,8 +407,7 @@ class GeoQueriesTest(MongoDBTestCase):
 | 
			
		||||
        self.assertEqual(1, roads)
 | 
			
		||||
 | 
			
		||||
        # Intersects
 | 
			
		||||
        line = {"type": "LineString",
 | 
			
		||||
                "coordinates": [[40, 5], [40, 6]]}
 | 
			
		||||
        line = {"type": "LineString", "coordinates": [[40, 5], [40, 6]]}
 | 
			
		||||
        roads = Road.objects.filter(line__geo_intersects=line["coordinates"]).count()
 | 
			
		||||
        self.assertEqual(1, roads)
 | 
			
		||||
 | 
			
		||||
@@ -436,8 +417,10 @@ class GeoQueriesTest(MongoDBTestCase):
 | 
			
		||||
        roads = Road.objects.filter(line__geo_intersects={"$geometry": line}).count()
 | 
			
		||||
        self.assertEqual(1, roads)
 | 
			
		||||
 | 
			
		||||
        polygon = {"type": "Polygon",
 | 
			
		||||
                   "coordinates": [[[40, 5], [40, 6], [41, 6], [41, 5], [40, 5]]]}
 | 
			
		||||
        polygon = {
 | 
			
		||||
            "type": "Polygon",
 | 
			
		||||
            "coordinates": [[[40, 5], [40, 6], [41, 6], [41, 5], [40, 5]]],
 | 
			
		||||
        }
 | 
			
		||||
        roads = Road.objects.filter(line__geo_intersects=polygon["coordinates"]).count()
 | 
			
		||||
        self.assertEqual(1, roads)
 | 
			
		||||
 | 
			
		||||
@@ -468,8 +451,10 @@ class GeoQueriesTest(MongoDBTestCase):
 | 
			
		||||
        self.assertEqual(1, roads)
 | 
			
		||||
 | 
			
		||||
        # Within
 | 
			
		||||
        polygon = {"type": "Polygon",
 | 
			
		||||
                   "coordinates": [[[40, 5], [40, 6], [41, 6], [41, 5], [40, 5]]]}
 | 
			
		||||
        polygon = {
 | 
			
		||||
            "type": "Polygon",
 | 
			
		||||
            "coordinates": [[[40, 5], [40, 6], [41, 6], [41, 5], [40, 5]]],
 | 
			
		||||
        }
 | 
			
		||||
        roads = Road.objects.filter(poly__geo_within=polygon["coordinates"]).count()
 | 
			
		||||
        self.assertEqual(1, roads)
 | 
			
		||||
 | 
			
		||||
@@ -480,8 +465,7 @@ class GeoQueriesTest(MongoDBTestCase):
 | 
			
		||||
        self.assertEqual(1, roads)
 | 
			
		||||
 | 
			
		||||
        # Intersects
 | 
			
		||||
        line = {"type": "LineString",
 | 
			
		||||
                "coordinates": [[40, 5], [41, 6]]}
 | 
			
		||||
        line = {"type": "LineString", "coordinates": [[40, 5], [41, 6]]}
 | 
			
		||||
        roads = Road.objects.filter(poly__geo_intersects=line["coordinates"]).count()
 | 
			
		||||
        self.assertEqual(1, roads)
 | 
			
		||||
 | 
			
		||||
@@ -491,8 +475,10 @@ class GeoQueriesTest(MongoDBTestCase):
 | 
			
		||||
        roads = Road.objects.filter(poly__geo_intersects={"$geometry": line}).count()
 | 
			
		||||
        self.assertEqual(1, roads)
 | 
			
		||||
 | 
			
		||||
        polygon = {"type": "Polygon",
 | 
			
		||||
                   "coordinates": [[[40, 5], [40, 6], [41, 6], [41, 5], [40, 5]]]}
 | 
			
		||||
        polygon = {
 | 
			
		||||
            "type": "Polygon",
 | 
			
		||||
            "coordinates": [[[40, 5], [40, 6], [41, 6], [41, 5], [40, 5]]],
 | 
			
		||||
        }
 | 
			
		||||
        roads = Road.objects.filter(poly__geo_intersects=polygon["coordinates"]).count()
 | 
			
		||||
        self.assertEqual(1, roads)
 | 
			
		||||
 | 
			
		||||
@@ -504,20 +490,20 @@ class GeoQueriesTest(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
    def test_aspymongo_with_only(self):
 | 
			
		||||
        """Ensure as_pymongo works with only"""
 | 
			
		||||
 | 
			
		||||
        class Place(Document):
 | 
			
		||||
            location = PointField()
 | 
			
		||||
 | 
			
		||||
        Place.drop_collection()
 | 
			
		||||
        p = Place(location=[24.946861267089844, 60.16311983618494])
 | 
			
		||||
        p.save()
 | 
			
		||||
        qs = Place.objects().only('location')
 | 
			
		||||
        qs = Place.objects().only("location")
 | 
			
		||||
        self.assertDictEqual(
 | 
			
		||||
            qs.as_pymongo()[0]['location'],
 | 
			
		||||
            {u'type': u'Point',
 | 
			
		||||
             u'coordinates': [
 | 
			
		||||
                24.946861267089844,
 | 
			
		||||
                60.16311983618494]
 | 
			
		||||
            }
 | 
			
		||||
            qs.as_pymongo()[0]["location"],
 | 
			
		||||
            {
 | 
			
		||||
                u"type": u"Point",
 | 
			
		||||
                u"coordinates": [24.946861267089844, 60.16311983618494],
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_2dsphere_point_sets_correctly(self):
 | 
			
		||||
@@ -542,11 +528,15 @@ class GeoQueriesTest(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        Location(line=[[1, 2], [2, 2]]).save()
 | 
			
		||||
        loc = Location.objects.as_pymongo()[0]
 | 
			
		||||
        self.assertEqual(loc["line"], {"type": "LineString", "coordinates": [[1, 2], [2, 2]]})
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            loc["line"], {"type": "LineString", "coordinates": [[1, 2], [2, 2]]}
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        Location.objects.update(set__line=[[2, 1], [1, 2]])
 | 
			
		||||
        loc = Location.objects.as_pymongo()[0]
 | 
			
		||||
        self.assertEqual(loc["line"], {"type": "LineString", "coordinates": [[2, 1], [1, 2]]})
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            loc["line"], {"type": "LineString", "coordinates": [[2, 1], [1, 2]]}
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_geojson_PolygonField(self):
 | 
			
		||||
        class Location(Document):
 | 
			
		||||
@@ -556,12 +546,18 @@ class GeoQueriesTest(MongoDBTestCase):
 | 
			
		||||
 | 
			
		||||
        Location(poly=[[[40, 5], [40, 6], [41, 6], [40, 5]]]).save()
 | 
			
		||||
        loc = Location.objects.as_pymongo()[0]
 | 
			
		||||
        self.assertEqual(loc["poly"], {"type": "Polygon", "coordinates": [[[40, 5], [40, 6], [41, 6], [40, 5]]]})
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            loc["poly"],
 | 
			
		||||
            {"type": "Polygon", "coordinates": [[[40, 5], [40, 6], [41, 6], [40, 5]]]},
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        Location.objects.update(set__poly=[[[40, 4], [40, 6], [41, 6], [40, 4]]])
 | 
			
		||||
        loc = Location.objects.as_pymongo()[0]
 | 
			
		||||
        self.assertEqual(loc["poly"], {"type": "Polygon", "coordinates": [[[40, 4], [40, 6], [41, 6], [40, 4]]]})
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            loc["poly"],
 | 
			
		||||
            {"type": "Polygon", "coordinates": [[[40, 4], [40, 6], [41, 6], [40, 4]]]},
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    unittest.main()
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,6 @@ class Doc(Document):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FindAndModifyTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        connect(db="mongoenginetest")
 | 
			
		||||
        Doc.drop_collection()
 | 
			
		||||
@@ -82,9 +81,14 @@ class FindAndModifyTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
        old_doc = Doc.objects().order_by("-id").modify(set__value=-1)
 | 
			
		||||
        self.assertEqual(old_doc.to_json(), doc.to_json())
 | 
			
		||||
        self.assertDbEqual([
 | 
			
		||||
            {"_id": 0, "value": 3}, {"_id": 1, "value": 2},
 | 
			
		||||
            {"_id": 2, "value": 1}, {"_id": 3, "value": -1}])
 | 
			
		||||
        self.assertDbEqual(
 | 
			
		||||
            [
 | 
			
		||||
                {"_id": 0, "value": 3},
 | 
			
		||||
                {"_id": 1, "value": 2},
 | 
			
		||||
                {"_id": 2, "value": 1},
 | 
			
		||||
                {"_id": 3, "value": -1},
 | 
			
		||||
            ]
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_modify_with_fields(self):
 | 
			
		||||
        Doc(id=0, value=0).save()
 | 
			
		||||
@@ -103,27 +107,25 @@ class FindAndModifyTest(unittest.TestCase):
 | 
			
		||||
        blog = BlogPost.objects.create()
 | 
			
		||||
 | 
			
		||||
        # Push a new tag via modify with new=False (default).
 | 
			
		||||
        BlogPost(id=blog.id).modify(push__tags='code')
 | 
			
		||||
        BlogPost(id=blog.id).modify(push__tags="code")
 | 
			
		||||
        self.assertEqual(blog.tags, [])
 | 
			
		||||
        blog.reload()
 | 
			
		||||
        self.assertEqual(blog.tags, ['code'])
 | 
			
		||||
        self.assertEqual(blog.tags, ["code"])
 | 
			
		||||
 | 
			
		||||
        # Push a new tag via modify with new=True.
 | 
			
		||||
        blog = BlogPost.objects(id=blog.id).modify(push__tags='java', new=True)
 | 
			
		||||
        self.assertEqual(blog.tags, ['code', 'java'])
 | 
			
		||||
        blog = BlogPost.objects(id=blog.id).modify(push__tags="java", new=True)
 | 
			
		||||
        self.assertEqual(blog.tags, ["code", "java"])
 | 
			
		||||
 | 
			
		||||
        # Push a new tag with a positional argument.
 | 
			
		||||
        blog = BlogPost.objects(id=blog.id).modify(
 | 
			
		||||
            push__tags__0='python',
 | 
			
		||||
            new=True)
 | 
			
		||||
        self.assertEqual(blog.tags, ['python', 'code', 'java'])
 | 
			
		||||
        blog = BlogPost.objects(id=blog.id).modify(push__tags__0="python", new=True)
 | 
			
		||||
        self.assertEqual(blog.tags, ["python", "code", "java"])
 | 
			
		||||
 | 
			
		||||
        # Push multiple new tags with a positional argument.
 | 
			
		||||
        blog = BlogPost.objects(id=blog.id).modify(
 | 
			
		||||
            push__tags__1=['go', 'rust'],
 | 
			
		||||
            new=True)
 | 
			
		||||
        self.assertEqual(blog.tags, ['python', 'go', 'rust', 'code', 'java'])
 | 
			
		||||
            push__tags__1=["go", "rust"], new=True
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(blog.tags, ["python", "go", "rust", "code", "java"])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    unittest.main()
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ from pymongo.mongo_client import MongoClient
 | 
			
		||||
from mongoengine import Document, StringField, IntField
 | 
			
		||||
from mongoengine.connection import connect
 | 
			
		||||
 | 
			
		||||
__author__ = 'stas'
 | 
			
		||||
__author__ = "stas"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Person(Document):
 | 
			
		||||
@@ -17,6 +17,7 @@ class TestQuerysetPickable(unittest.TestCase):
 | 
			
		||||
    Test for adding pickling support for QuerySet instances
 | 
			
		||||
    See issue https://github.com/MongoEngine/mongoengine/issues/442
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(TestQuerysetPickable, self).setUp()
 | 
			
		||||
 | 
			
		||||
@@ -24,10 +25,7 @@ class TestQuerysetPickable(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
        connection.drop_database("test")
 | 
			
		||||
 | 
			
		||||
        self.john = Person.objects.create(
 | 
			
		||||
            name="John",
 | 
			
		||||
            age=21
 | 
			
		||||
        )
 | 
			
		||||
        self.john = Person.objects.create(name="John", age=21)
 | 
			
		||||
 | 
			
		||||
    def test_picke_simple_qs(self):
 | 
			
		||||
 | 
			
		||||
@@ -54,15 +52,9 @@ class TestQuerysetPickable(unittest.TestCase):
 | 
			
		||||
        self.assertEqual(Person.objects.first().age, 23)
 | 
			
		||||
 | 
			
		||||
    def test_pickle_support_filtration(self):
 | 
			
		||||
        Person.objects.create(
 | 
			
		||||
            name="Alice",
 | 
			
		||||
            age=22
 | 
			
		||||
        )
 | 
			
		||||
        Person.objects.create(name="Alice", age=22)
 | 
			
		||||
 | 
			
		||||
        Person.objects.create(
 | 
			
		||||
            name="Bob",
 | 
			
		||||
            age=23
 | 
			
		||||
        )
 | 
			
		||||
        Person.objects.create(name="Bob", age=23)
 | 
			
		||||
 | 
			
		||||
        qs = Person.objects.filter(age__gte=22)
 | 
			
		||||
        self.assertEqual(qs.count(), 2)
 | 
			
		||||
@@ -71,9 +63,3 @@ class TestQuerysetPickable(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(loaded.count(), 2)
 | 
			
		||||
        self.assertEqual(loaded.filter(name="Bob").first().age, 23)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -9,25 +9,29 @@ __all__ = ("TransformTest",)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TransformTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        connect(db='mongoenginetest')
 | 
			
		||||
        connect(db="mongoenginetest")
 | 
			
		||||
 | 
			
		||||
    def test_transform_query(self):
 | 
			
		||||
        """Ensure that the _transform_query function operates correctly.
 | 
			
		||||
        """
 | 
			
		||||
        self.assertEqual(transform.query(name='test', age=30),
 | 
			
		||||
                         {'name': 'test', 'age': 30})
 | 
			
		||||
        self.assertEqual(transform.query(age__lt=30),
 | 
			
		||||
                         {'age': {'$lt': 30}})
 | 
			
		||||
        self.assertEqual(transform.query(age__gt=20, age__lt=50),
 | 
			
		||||
                         {'age': {'$gt': 20, '$lt': 50}})
 | 
			
		||||
        self.assertEqual(transform.query(age=20, age__gt=50),
 | 
			
		||||
                         {'$and': [{'age': {'$gt': 50}}, {'age': 20}]})
 | 
			
		||||
        self.assertEqual(transform.query(friend__age__gte=30),
 | 
			
		||||
                         {'friend.age': {'$gte': 30}})
 | 
			
		||||
        self.assertEqual(transform.query(name__exists=True),
 | 
			
		||||
                         {'name': {'$exists': True}})
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            transform.query(name="test", age=30), {"name": "test", "age": 30}
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(transform.query(age__lt=30), {"age": {"$lt": 30}})
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            transform.query(age__gt=20, age__lt=50), {"age": {"$gt": 20, "$lt": 50}}
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            transform.query(age=20, age__gt=50),
 | 
			
		||||
            {"$and": [{"age": {"$gt": 50}}, {"age": 20}]},
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            transform.query(friend__age__gte=30), {"friend.age": {"$gte": 30}}
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            transform.query(name__exists=True), {"name": {"$exists": True}}
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_transform_update(self):
 | 
			
		||||
        class LisDoc(Document):
 | 
			
		||||
@@ -46,7 +50,11 @@ class TransformTest(unittest.TestCase):
 | 
			
		||||
        DicDoc().save()
 | 
			
		||||
        doc = Doc().save()
 | 
			
		||||
 | 
			
		||||
        for k, v in (("set", "$set"), ("set_on_insert", "$setOnInsert"), ("push", "$push")):
 | 
			
		||||
        for k, v in (
 | 
			
		||||
            ("set", "$set"),
 | 
			
		||||
            ("set_on_insert", "$setOnInsert"),
 | 
			
		||||
            ("push", "$push"),
 | 
			
		||||
        ):
 | 
			
		||||
            update = transform.update(DicDoc, **{"%s__dictField__test" % k: doc})
 | 
			
		||||
            self.assertIsInstance(update[v]["dictField.test"], dict)
 | 
			
		||||
 | 
			
		||||
@@ -57,55 +65,61 @@ class TransformTest(unittest.TestCase):
 | 
			
		||||
        update = transform.update(DicDoc, pull__dictField__test=doc)
 | 
			
		||||
        self.assertIsInstance(update["$pull"]["dictField"]["test"], dict)
 | 
			
		||||
 | 
			
		||||
        update = transform.update(LisDoc, pull__foo__in=['a'])
 | 
			
		||||
        self.assertEqual(update, {'$pull': {'foo': {'$in': ['a']}}})
 | 
			
		||||
        update = transform.update(LisDoc, pull__foo__in=["a"])
 | 
			
		||||
        self.assertEqual(update, {"$pull": {"foo": {"$in": ["a"]}}})
 | 
			
		||||
 | 
			
		||||
    def test_transform_update_push(self):
 | 
			
		||||
        """Ensure the differences in behvaior between 'push' and 'push_all'"""
 | 
			
		||||
 | 
			
		||||
        class BlogPost(Document):
 | 
			
		||||
            tags = ListField(StringField())
 | 
			
		||||
 | 
			
		||||
        update = transform.update(BlogPost, push__tags=['mongo', 'db'])
 | 
			
		||||
        self.assertEqual(update, {'$push': {'tags': ['mongo', 'db']}})
 | 
			
		||||
        update = transform.update(BlogPost, push__tags=["mongo", "db"])
 | 
			
		||||
        self.assertEqual(update, {"$push": {"tags": ["mongo", "db"]}})
 | 
			
		||||
 | 
			
		||||
        update = transform.update(BlogPost, push_all__tags=['mongo', 'db'])
 | 
			
		||||
        self.assertEqual(update, {'$push': {'tags': {'$each': ['mongo', 'db']}}})
 | 
			
		||||
        update = transform.update(BlogPost, push_all__tags=["mongo", "db"])
 | 
			
		||||
        self.assertEqual(update, {"$push": {"tags": {"$each": ["mongo", "db"]}}})
 | 
			
		||||
 | 
			
		||||
    def test_transform_update_no_operator_default_to_set(self):
 | 
			
		||||
        """Ensure the differences in behvaior between 'push' and 'push_all'"""
 | 
			
		||||
 | 
			
		||||
        class BlogPost(Document):
 | 
			
		||||
            tags = ListField(StringField())
 | 
			
		||||
 | 
			
		||||
        update = transform.update(BlogPost, tags=['mongo', 'db'])
 | 
			
		||||
        self.assertEqual(update, {'$set': {'tags': ['mongo', 'db']}})
 | 
			
		||||
        update = transform.update(BlogPost, tags=["mongo", "db"])
 | 
			
		||||
        self.assertEqual(update, {"$set": {"tags": ["mongo", "db"]}})
 | 
			
		||||
 | 
			
		||||
    def test_query_field_name(self):
 | 
			
		||||
        """Ensure that the correct field name is used when querying.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Comment(EmbeddedDocument):
 | 
			
		||||
            content = StringField(db_field='commentContent')
 | 
			
		||||
            content = StringField(db_field="commentContent")
 | 
			
		||||
 | 
			
		||||
        class BlogPost(Document):
 | 
			
		||||
            title = StringField(db_field='postTitle')
 | 
			
		||||
            comments = ListField(EmbeddedDocumentField(Comment),
 | 
			
		||||
                                 db_field='postComments')
 | 
			
		||||
            title = StringField(db_field="postTitle")
 | 
			
		||||
            comments = ListField(
 | 
			
		||||
                EmbeddedDocumentField(Comment), db_field="postComments"
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        BlogPost.drop_collection()
 | 
			
		||||
 | 
			
		||||
        data = {'title': 'Post 1', 'comments': [Comment(content='test')]}
 | 
			
		||||
        data = {"title": "Post 1", "comments": [Comment(content="test")]}
 | 
			
		||||
        post = BlogPost(**data)
 | 
			
		||||
        post.save()
 | 
			
		||||
 | 
			
		||||
        self.assertIn('postTitle', BlogPost.objects(title=data['title'])._query)
 | 
			
		||||
        self.assertFalse('title' in
 | 
			
		||||
                         BlogPost.objects(title=data['title'])._query)
 | 
			
		||||
        self.assertEqual(BlogPost.objects(title=data['title']).count(), 1)
 | 
			
		||||
        self.assertIn("postTitle", BlogPost.objects(title=data["title"])._query)
 | 
			
		||||
        self.assertFalse("title" in BlogPost.objects(title=data["title"])._query)
 | 
			
		||||
        self.assertEqual(BlogPost.objects(title=data["title"]).count(), 1)
 | 
			
		||||
 | 
			
		||||
        self.assertIn('_id', BlogPost.objects(pk=post.id)._query)
 | 
			
		||||
        self.assertIn("_id", BlogPost.objects(pk=post.id)._query)
 | 
			
		||||
        self.assertEqual(BlogPost.objects(pk=post.id).count(), 1)
 | 
			
		||||
 | 
			
		||||
        self.assertIn('postComments.commentContent', BlogPost.objects(comments__content='test')._query)
 | 
			
		||||
        self.assertEqual(BlogPost.objects(comments__content='test').count(), 1)
 | 
			
		||||
        self.assertIn(
 | 
			
		||||
            "postComments.commentContent",
 | 
			
		||||
            BlogPost.objects(comments__content="test")._query,
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(BlogPost.objects(comments__content="test").count(), 1)
 | 
			
		||||
 | 
			
		||||
        BlogPost.drop_collection()
 | 
			
		||||
 | 
			
		||||
@@ -113,18 +127,19 @@ class TransformTest(unittest.TestCase):
 | 
			
		||||
        """Ensure that the correct "primary key" field name is used when
 | 
			
		||||
        querying
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class BlogPost(Document):
 | 
			
		||||
            title = StringField(primary_key=True, db_field='postTitle')
 | 
			
		||||
            title = StringField(primary_key=True, db_field="postTitle")
 | 
			
		||||
 | 
			
		||||
        BlogPost.drop_collection()
 | 
			
		||||
 | 
			
		||||
        data = {'title': 'Post 1'}
 | 
			
		||||
        data = {"title": "Post 1"}
 | 
			
		||||
        post = BlogPost(**data)
 | 
			
		||||
        post.save()
 | 
			
		||||
 | 
			
		||||
        self.assertIn('_id', BlogPost.objects(pk=data['title'])._query)
 | 
			
		||||
        self.assertIn('_id', BlogPost.objects(title=data['title'])._query)
 | 
			
		||||
        self.assertEqual(BlogPost.objects(pk=data['title']).count(), 1)
 | 
			
		||||
        self.assertIn("_id", BlogPost.objects(pk=data["title"])._query)
 | 
			
		||||
        self.assertIn("_id", BlogPost.objects(title=data["title"])._query)
 | 
			
		||||
        self.assertEqual(BlogPost.objects(pk=data["title"]).count(), 1)
 | 
			
		||||
 | 
			
		||||
        BlogPost.drop_collection()
 | 
			
		||||
 | 
			
		||||
@@ -156,78 +171,125 @@ class TransformTest(unittest.TestCase):
 | 
			
		||||
        """
 | 
			
		||||
        Test raw plays nicely
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Foo(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            a = StringField()
 | 
			
		||||
            b = StringField()
 | 
			
		||||
            c = StringField()
 | 
			
		||||
 | 
			
		||||
            meta = {
 | 
			
		||||
                'allow_inheritance': False
 | 
			
		||||
            }
 | 
			
		||||
            meta = {"allow_inheritance": False}
 | 
			
		||||
 | 
			
		||||
        query = Foo.objects(__raw__={'$nor': [{'name': 'bar'}]})._query
 | 
			
		||||
        self.assertEqual(query, {'$nor': [{'name': 'bar'}]})
 | 
			
		||||
        query = Foo.objects(__raw__={"$nor": [{"name": "bar"}]})._query
 | 
			
		||||
        self.assertEqual(query, {"$nor": [{"name": "bar"}]})
 | 
			
		||||
 | 
			
		||||
        q1 = {'$or': [{'a': 1}, {'b': 1}]}
 | 
			
		||||
        q1 = {"$or": [{"a": 1}, {"b": 1}]}
 | 
			
		||||
        query = Foo.objects(Q(__raw__=q1) & Q(c=1))._query
 | 
			
		||||
        self.assertEqual(query, {'$or': [{'a': 1}, {'b': 1}], 'c': 1})
 | 
			
		||||
        self.assertEqual(query, {"$or": [{"a": 1}, {"b": 1}], "c": 1})
 | 
			
		||||
 | 
			
		||||
    def test_raw_and_merging(self):
 | 
			
		||||
        class Doc(Document):
 | 
			
		||||
            meta = {'allow_inheritance': False}
 | 
			
		||||
            meta = {"allow_inheritance": False}
 | 
			
		||||
 | 
			
		||||
        raw_query = Doc.objects(__raw__={
 | 
			
		||||
            'deleted': False,
 | 
			
		||||
            'scraped': 'yes',
 | 
			
		||||
            '$nor': [
 | 
			
		||||
                {'views.extracted': 'no'},
 | 
			
		||||
                {'attachments.views.extracted': 'no'}
 | 
			
		||||
            ]
 | 
			
		||||
        })._query
 | 
			
		||||
        raw_query = Doc.objects(
 | 
			
		||||
            __raw__={
 | 
			
		||||
                "deleted": False,
 | 
			
		||||
                "scraped": "yes",
 | 
			
		||||
                "$nor": [
 | 
			
		||||
                    {"views.extracted": "no"},
 | 
			
		||||
                    {"attachments.views.extracted": "no"},
 | 
			
		||||
                ],
 | 
			
		||||
            }
 | 
			
		||||
        )._query
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(raw_query, {
 | 
			
		||||
            'deleted': False,
 | 
			
		||||
            'scraped': 'yes',
 | 
			
		||||
            '$nor': [
 | 
			
		||||
                {'views.extracted': 'no'},
 | 
			
		||||
                {'attachments.views.extracted': 'no'}
 | 
			
		||||
            ]
 | 
			
		||||
        })
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            raw_query,
 | 
			
		||||
            {
 | 
			
		||||
                "deleted": False,
 | 
			
		||||
                "scraped": "yes",
 | 
			
		||||
                "$nor": [
 | 
			
		||||
                    {"views.extracted": "no"},
 | 
			
		||||
                    {"attachments.views.extracted": "no"},
 | 
			
		||||
                ],
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_geojson_PointField(self):
 | 
			
		||||
        class Location(Document):
 | 
			
		||||
            loc = PointField()
 | 
			
		||||
 | 
			
		||||
        update = transform.update(Location, set__loc=[1, 2])
 | 
			
		||||
        self.assertEqual(update, {'$set': {'loc': {"type": "Point", "coordinates": [1, 2]}}})
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            update, {"$set": {"loc": {"type": "Point", "coordinates": [1, 2]}}}
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        update = transform.update(Location, set__loc={"type": "Point", "coordinates": [1, 2]})
 | 
			
		||||
        self.assertEqual(update, {'$set': {'loc': {"type": "Point", "coordinates": [1, 2]}}})
 | 
			
		||||
        update = transform.update(
 | 
			
		||||
            Location, set__loc={"type": "Point", "coordinates": [1, 2]}
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            update, {"$set": {"loc": {"type": "Point", "coordinates": [1, 2]}}}
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_geojson_LineStringField(self):
 | 
			
		||||
        class Location(Document):
 | 
			
		||||
            line = LineStringField()
 | 
			
		||||
 | 
			
		||||
        update = transform.update(Location, set__line=[[1, 2], [2, 2]])
 | 
			
		||||
        self.assertEqual(update, {'$set': {'line': {"type": "LineString", "coordinates": [[1, 2], [2, 2]]}}})
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            update,
 | 
			
		||||
            {"$set": {"line": {"type": "LineString", "coordinates": [[1, 2], [2, 2]]}}},
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        update = transform.update(Location, set__line={"type": "LineString", "coordinates": [[1, 2], [2, 2]]})
 | 
			
		||||
        self.assertEqual(update, {'$set': {'line': {"type": "LineString", "coordinates": [[1, 2], [2, 2]]}}})
 | 
			
		||||
        update = transform.update(
 | 
			
		||||
            Location, set__line={"type": "LineString", "coordinates": [[1, 2], [2, 2]]}
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            update,
 | 
			
		||||
            {"$set": {"line": {"type": "LineString", "coordinates": [[1, 2], [2, 2]]}}},
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_geojson_PolygonField(self):
 | 
			
		||||
        class Location(Document):
 | 
			
		||||
            poly = PolygonField()
 | 
			
		||||
 | 
			
		||||
        update = transform.update(Location, set__poly=[[[40, 5], [40, 6], [41, 6], [40, 5]]])
 | 
			
		||||
        self.assertEqual(update, {'$set': {'poly': {"type": "Polygon", "coordinates": [[[40, 5], [40, 6], [41, 6], [40, 5]]]}}})
 | 
			
		||||
        update = transform.update(
 | 
			
		||||
            Location, set__poly=[[[40, 5], [40, 6], [41, 6], [40, 5]]]
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            update,
 | 
			
		||||
            {
 | 
			
		||||
                "$set": {
 | 
			
		||||
                    "poly": {
 | 
			
		||||
                        "type": "Polygon",
 | 
			
		||||
                        "coordinates": [[[40, 5], [40, 6], [41, 6], [40, 5]]],
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        update = transform.update(Location, set__poly={"type": "Polygon", "coordinates": [[[40, 5], [40, 6], [41, 6], [40, 5]]]})
 | 
			
		||||
        self.assertEqual(update, {'$set': {'poly': {"type": "Polygon", "coordinates": [[[40, 5], [40, 6], [41, 6], [40, 5]]]}}})
 | 
			
		||||
        update = transform.update(
 | 
			
		||||
            Location,
 | 
			
		||||
            set__poly={
 | 
			
		||||
                "type": "Polygon",
 | 
			
		||||
                "coordinates": [[[40, 5], [40, 6], [41, 6], [40, 5]]],
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            update,
 | 
			
		||||
            {
 | 
			
		||||
                "$set": {
 | 
			
		||||
                    "poly": {
 | 
			
		||||
                        "type": "Polygon",
 | 
			
		||||
                        "coordinates": [[[40, 5], [40, 6], [41, 6], [40, 5]]],
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_type(self):
 | 
			
		||||
        class Doc(Document):
 | 
			
		||||
            df = DynamicField()
 | 
			
		||||
 | 
			
		||||
        Doc(df=True).save()
 | 
			
		||||
        Doc(df=7).save()
 | 
			
		||||
        Doc(df="df").save()
 | 
			
		||||
@@ -252,7 +314,7 @@ class TransformTest(unittest.TestCase):
 | 
			
		||||
        self.assertEqual(1, Doc.objects(item__type__="axe").count())
 | 
			
		||||
        self.assertEqual(1, Doc.objects(item__name__="Heroic axe").count())
 | 
			
		||||
 | 
			
		||||
        Doc.objects(id=doc.id).update(set__item__type__='sword')
 | 
			
		||||
        Doc.objects(id=doc.id).update(set__item__type__="sword")
 | 
			
		||||
        self.assertEqual(1, Doc.objects(item__type__="sword").count())
 | 
			
		||||
        self.assertEqual(0, Doc.objects(item__type__="axe").count())
 | 
			
		||||
 | 
			
		||||
@@ -272,6 +334,7 @@ class TransformTest(unittest.TestCase):
 | 
			
		||||
        Test added to check pull operation in update for
 | 
			
		||||
        EmbeddedDocumentListField which is inside a EmbeddedDocumentField
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Word(EmbeddedDocument):
 | 
			
		||||
            word = StringField()
 | 
			
		||||
            index = IntField()
 | 
			
		||||
@@ -284,18 +347,27 @@ class TransformTest(unittest.TestCase):
 | 
			
		||||
            title = StringField()
 | 
			
		||||
            content = EmbeddedDocumentField(SubDoc)
 | 
			
		||||
 | 
			
		||||
        word = Word(word='abc', index=1)
 | 
			
		||||
        word = Word(word="abc", index=1)
 | 
			
		||||
        update = transform.update(MainDoc, pull__content__text=word)
 | 
			
		||||
        self.assertEqual(update, {'$pull': {'content.text': SON([('word', u'abc'), ('index', 1)])}})
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            update, {"$pull": {"content.text": SON([("word", u"abc"), ("index", 1)])}}
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        update = transform.update(MainDoc, pull__content__heading='xyz')
 | 
			
		||||
        self.assertEqual(update, {'$pull': {'content.heading': 'xyz'}})
 | 
			
		||||
        update = transform.update(MainDoc, pull__content__heading="xyz")
 | 
			
		||||
        self.assertEqual(update, {"$pull": {"content.heading": "xyz"}})
 | 
			
		||||
 | 
			
		||||
        update = transform.update(MainDoc, pull__content__text__word__in=['foo', 'bar'])
 | 
			
		||||
        self.assertEqual(update, {'$pull': {'content.text': {'word': {'$in': ['foo', 'bar']}}}})
 | 
			
		||||
        update = transform.update(MainDoc, pull__content__text__word__in=["foo", "bar"])
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            update, {"$pull": {"content.text": {"word": {"$in": ["foo", "bar"]}}}}
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        update = transform.update(MainDoc, pull__content__text__word__nin=['foo', 'bar'])
 | 
			
		||||
        self.assertEqual(update, {'$pull': {'content.text': {'word': {'$nin': ['foo', 'bar']}}}})
 | 
			
		||||
        update = transform.update(
 | 
			
		||||
            MainDoc, pull__content__text__word__nin=["foo", "bar"]
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            update, {"$pull": {"content.text": {"word": {"$nin": ["foo", "bar"]}}}}
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    unittest.main()
 | 
			
		||||
 
 | 
			
		||||
@@ -12,14 +12,13 @@ __all__ = ("QTest",)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class QTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        connect(db='mongoenginetest')
 | 
			
		||||
        connect(db="mongoenginetest")
 | 
			
		||||
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            age = IntField()
 | 
			
		||||
            meta = {'allow_inheritance': True}
 | 
			
		||||
            meta = {"allow_inheritance": True}
 | 
			
		||||
 | 
			
		||||
        Person.drop_collection()
 | 
			
		||||
        self.Person = Person
 | 
			
		||||
@@ -30,22 +29,22 @@ class QTest(unittest.TestCase):
 | 
			
		||||
        q1 = Q()
 | 
			
		||||
        q2 = Q(age__gte=18)
 | 
			
		||||
        q3 = Q()
 | 
			
		||||
        q4 = Q(name='test')
 | 
			
		||||
        q4 = Q(name="test")
 | 
			
		||||
        q5 = Q()
 | 
			
		||||
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            age = IntField()
 | 
			
		||||
 | 
			
		||||
        query = {'$or': [{'age': {'$gte': 18}}, {'name': 'test'}]}
 | 
			
		||||
        query = {"$or": [{"age": {"$gte": 18}}, {"name": "test"}]}
 | 
			
		||||
        self.assertEqual((q1 | q2 | q3 | q4 | q5).to_query(Person), query)
 | 
			
		||||
 | 
			
		||||
        query = {'age': {'$gte': 18}, 'name': 'test'}
 | 
			
		||||
        query = {"age": {"$gte": 18}, "name": "test"}
 | 
			
		||||
        self.assertEqual((q1 & q2 & q3 & q4 & q5).to_query(Person), query)
 | 
			
		||||
 | 
			
		||||
    def test_q_with_dbref(self):
 | 
			
		||||
        """Ensure Q objects handle DBRefs correctly"""
 | 
			
		||||
        connect(db='mongoenginetest')
 | 
			
		||||
        connect(db="mongoenginetest")
 | 
			
		||||
 | 
			
		||||
        class User(Document):
 | 
			
		||||
            pass
 | 
			
		||||
@@ -62,15 +61,18 @@ class QTest(unittest.TestCase):
 | 
			
		||||
    def test_and_combination(self):
 | 
			
		||||
        """Ensure that Q-objects correctly AND together.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class TestDoc(Document):
 | 
			
		||||
            x = IntField()
 | 
			
		||||
            y = StringField()
 | 
			
		||||
 | 
			
		||||
        query = (Q(x__lt=7) & Q(x__lt=3)).to_query(TestDoc)
 | 
			
		||||
        self.assertEqual(query, {'$and': [{'x': {'$lt': 7}}, {'x': {'$lt': 3}}]})
 | 
			
		||||
        self.assertEqual(query, {"$and": [{"x": {"$lt": 7}}, {"x": {"$lt": 3}}]})
 | 
			
		||||
 | 
			
		||||
        query = (Q(y="a") & Q(x__lt=7) & Q(x__lt=3)).to_query(TestDoc)
 | 
			
		||||
        self.assertEqual(query, {'$and': [{'y': "a"}, {'x': {'$lt': 7}}, {'x': {'$lt': 3}}]})
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            query, {"$and": [{"y": "a"}, {"x": {"$lt": 7}}, {"x": {"$lt": 3}}]}
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # Check normal cases work without an error
 | 
			
		||||
        query = Q(x__lt=7) & Q(x__gt=3)
 | 
			
		||||
@@ -78,69 +80,74 @@ class QTest(unittest.TestCase):
 | 
			
		||||
        q1 = Q(x__lt=7)
 | 
			
		||||
        q2 = Q(x__gt=3)
 | 
			
		||||
        query = (q1 & q2).to_query(TestDoc)
 | 
			
		||||
        self.assertEqual(query, {'x': {'$lt': 7, '$gt': 3}})
 | 
			
		||||
        self.assertEqual(query, {"x": {"$lt": 7, "$gt": 3}})
 | 
			
		||||
 | 
			
		||||
        # More complex nested example
 | 
			
		||||
        query = Q(x__lt=100) & Q(y__ne='NotMyString')
 | 
			
		||||
        query &= Q(y__in=['a', 'b', 'c']) & Q(x__gt=-100)
 | 
			
		||||
        query = Q(x__lt=100) & Q(y__ne="NotMyString")
 | 
			
		||||
        query &= Q(y__in=["a", "b", "c"]) & Q(x__gt=-100)
 | 
			
		||||
        mongo_query = {
 | 
			
		||||
            'x': {'$lt': 100, '$gt': -100},
 | 
			
		||||
            'y': {'$ne': 'NotMyString', '$in': ['a', 'b', 'c']},
 | 
			
		||||
            "x": {"$lt": 100, "$gt": -100},
 | 
			
		||||
            "y": {"$ne": "NotMyString", "$in": ["a", "b", "c"]},
 | 
			
		||||
        }
 | 
			
		||||
        self.assertEqual(query.to_query(TestDoc), mongo_query)
 | 
			
		||||
 | 
			
		||||
    def test_or_combination(self):
 | 
			
		||||
        """Ensure that Q-objects correctly OR together.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class TestDoc(Document):
 | 
			
		||||
            x = IntField()
 | 
			
		||||
 | 
			
		||||
        q1 = Q(x__lt=3)
 | 
			
		||||
        q2 = Q(x__gt=7)
 | 
			
		||||
        query = (q1 | q2).to_query(TestDoc)
 | 
			
		||||
        self.assertEqual(query, {
 | 
			
		||||
            '$or': [
 | 
			
		||||
                {'x': {'$lt': 3}},
 | 
			
		||||
                {'x': {'$gt': 7}},
 | 
			
		||||
            ]
 | 
			
		||||
        })
 | 
			
		||||
        self.assertEqual(query, {"$or": [{"x": {"$lt": 3}}, {"x": {"$gt": 7}}]})
 | 
			
		||||
 | 
			
		||||
    def test_and_or_combination(self):
 | 
			
		||||
        """Ensure that Q-objects handle ANDing ORed components.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class TestDoc(Document):
 | 
			
		||||
            x = IntField()
 | 
			
		||||
            y = BooleanField()
 | 
			
		||||
 | 
			
		||||
        TestDoc.drop_collection()
 | 
			
		||||
 | 
			
		||||
        query = (Q(x__gt=0) | Q(x__exists=False))
 | 
			
		||||
        query = Q(x__gt=0) | Q(x__exists=False)
 | 
			
		||||
        query &= Q(x__lt=100)
 | 
			
		||||
        self.assertEqual(query.to_query(TestDoc), {'$and': [
 | 
			
		||||
            {'$or': [{'x': {'$gt': 0}},
 | 
			
		||||
                     {'x': {'$exists': False}}]},
 | 
			
		||||
            {'x': {'$lt': 100}}]
 | 
			
		||||
        })
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            query.to_query(TestDoc),
 | 
			
		||||
            {
 | 
			
		||||
                "$and": [
 | 
			
		||||
                    {"$or": [{"x": {"$gt": 0}}, {"x": {"$exists": False}}]},
 | 
			
		||||
                    {"x": {"$lt": 100}},
 | 
			
		||||
                ]
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        q1 = (Q(x__gt=0) | Q(x__exists=False))
 | 
			
		||||
        q2 = (Q(x__lt=100) | Q(y=True))
 | 
			
		||||
        q1 = Q(x__gt=0) | Q(x__exists=False)
 | 
			
		||||
        q2 = Q(x__lt=100) | Q(y=True)
 | 
			
		||||
        query = (q1 & q2).to_query(TestDoc)
 | 
			
		||||
 | 
			
		||||
        TestDoc(x=101).save()
 | 
			
		||||
        TestDoc(x=10).save()
 | 
			
		||||
        TestDoc(y=True).save()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(query, {
 | 
			
		||||
            '$and': [
 | 
			
		||||
                {'$or': [{'x': {'$gt': 0}}, {'x': {'$exists': False}}]},
 | 
			
		||||
                {'$or': [{'x': {'$lt': 100}}, {'y': True}]}
 | 
			
		||||
            ]
 | 
			
		||||
        })
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            query,
 | 
			
		||||
            {
 | 
			
		||||
                "$and": [
 | 
			
		||||
                    {"$or": [{"x": {"$gt": 0}}, {"x": {"$exists": False}}]},
 | 
			
		||||
                    {"$or": [{"x": {"$lt": 100}}, {"y": True}]},
 | 
			
		||||
                ]
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(2, TestDoc.objects(q1 & q2).count())
 | 
			
		||||
 | 
			
		||||
    def test_or_and_or_combination(self):
 | 
			
		||||
        """Ensure that Q-objects handle ORing ANDed ORed components. :)
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class TestDoc(Document):
 | 
			
		||||
            x = IntField()
 | 
			
		||||
            y = BooleanField()
 | 
			
		||||
@@ -151,18 +158,29 @@ class QTest(unittest.TestCase):
 | 
			
		||||
        TestDoc(x=99, y=False).save()
 | 
			
		||||
        TestDoc(x=101, y=False).save()
 | 
			
		||||
 | 
			
		||||
        q1 = (Q(x__gt=0) & (Q(y=True) | Q(y__exists=False)))
 | 
			
		||||
        q2 = (Q(x__lt=100) & (Q(y=False) | Q(y__exists=False)))
 | 
			
		||||
        q1 = Q(x__gt=0) & (Q(y=True) | Q(y__exists=False))
 | 
			
		||||
        q2 = Q(x__lt=100) & (Q(y=False) | Q(y__exists=False))
 | 
			
		||||
        query = (q1 | q2).to_query(TestDoc)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(query, {
 | 
			
		||||
            '$or': [
 | 
			
		||||
                {'$and': [{'x': {'$gt': 0}},
 | 
			
		||||
                          {'$or': [{'y': True}, {'y': {'$exists': False}}]}]},
 | 
			
		||||
                {'$and': [{'x': {'$lt': 100}},
 | 
			
		||||
                          {'$or': [{'y': False}, {'y': {'$exists': False}}]}]}
 | 
			
		||||
            ]
 | 
			
		||||
        })
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            query,
 | 
			
		||||
            {
 | 
			
		||||
                "$or": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "$and": [
 | 
			
		||||
                            {"x": {"$gt": 0}},
 | 
			
		||||
                            {"$or": [{"y": True}, {"y": {"$exists": False}}]},
 | 
			
		||||
                        ]
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        "$and": [
 | 
			
		||||
                            {"x": {"$lt": 100}},
 | 
			
		||||
                            {"$or": [{"y": False}, {"y": {"$exists": False}}]},
 | 
			
		||||
                        ]
 | 
			
		||||
                    },
 | 
			
		||||
                ]
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(2, TestDoc.objects(q1 | q2).count())
 | 
			
		||||
 | 
			
		||||
    def test_multiple_occurence_in_field(self):
 | 
			
		||||
@@ -170,8 +188,8 @@ class QTest(unittest.TestCase):
 | 
			
		||||
            name = StringField(max_length=40)
 | 
			
		||||
            title = StringField(max_length=40)
 | 
			
		||||
 | 
			
		||||
        q1 = Q(name__contains='te') | Q(title__contains='te')
 | 
			
		||||
        q2 = Q(name__contains='12') | Q(title__contains='12')
 | 
			
		||||
        q1 = Q(name__contains="te") | Q(title__contains="te")
 | 
			
		||||
        q2 = Q(name__contains="12") | Q(title__contains="12")
 | 
			
		||||
 | 
			
		||||
        q3 = q1 & q2
 | 
			
		||||
 | 
			
		||||
@@ -180,7 +198,6 @@ class QTest(unittest.TestCase):
 | 
			
		||||
        self.assertEqual(query["$and"][1], q2.to_query(Test))
 | 
			
		||||
 | 
			
		||||
    def test_q_clone(self):
 | 
			
		||||
 | 
			
		||||
        class TestDoc(Document):
 | 
			
		||||
            x = IntField()
 | 
			
		||||
 | 
			
		||||
@@ -205,6 +222,7 @@ class QTest(unittest.TestCase):
 | 
			
		||||
    def test_q(self):
 | 
			
		||||
        """Ensure that Q objects may be used to query for documents.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class BlogPost(Document):
 | 
			
		||||
            title = StringField()
 | 
			
		||||
            publish_date = DateTimeField()
 | 
			
		||||
@@ -212,22 +230,26 @@ class QTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
        BlogPost.drop_collection()
 | 
			
		||||
 | 
			
		||||
        post1 = BlogPost(title='Test 1', publish_date=datetime.datetime(2010, 1, 8), published=False)
 | 
			
		||||
        post1 = BlogPost(
 | 
			
		||||
            title="Test 1", publish_date=datetime.datetime(2010, 1, 8), published=False
 | 
			
		||||
        )
 | 
			
		||||
        post1.save()
 | 
			
		||||
 | 
			
		||||
        post2 = BlogPost(title='Test 2', publish_date=datetime.datetime(2010, 1, 15), published=True)
 | 
			
		||||
        post2 = BlogPost(
 | 
			
		||||
            title="Test 2", publish_date=datetime.datetime(2010, 1, 15), published=True
 | 
			
		||||
        )
 | 
			
		||||
        post2.save()
 | 
			
		||||
 | 
			
		||||
        post3 = BlogPost(title='Test 3', published=True)
 | 
			
		||||
        post3 = BlogPost(title="Test 3", published=True)
 | 
			
		||||
        post3.save()
 | 
			
		||||
 | 
			
		||||
        post4 = BlogPost(title='Test 4', publish_date=datetime.datetime(2010, 1, 8))
 | 
			
		||||
        post4 = BlogPost(title="Test 4", publish_date=datetime.datetime(2010, 1, 8))
 | 
			
		||||
        post4.save()
 | 
			
		||||
 | 
			
		||||
        post5 = BlogPost(title='Test 1', publish_date=datetime.datetime(2010, 1, 15))
 | 
			
		||||
        post5 = BlogPost(title="Test 1", publish_date=datetime.datetime(2010, 1, 15))
 | 
			
		||||
        post5.save()
 | 
			
		||||
 | 
			
		||||
        post6 = BlogPost(title='Test 1', published=False)
 | 
			
		||||
        post6 = BlogPost(title="Test 1", published=False)
 | 
			
		||||
        post6.save()
 | 
			
		||||
 | 
			
		||||
        # Check ObjectId lookup works
 | 
			
		||||
@@ -235,13 +257,13 @@ class QTest(unittest.TestCase):
 | 
			
		||||
        self.assertEqual(obj, post1)
 | 
			
		||||
 | 
			
		||||
        # Check Q object combination with one does not exist
 | 
			
		||||
        q = BlogPost.objects(Q(title='Test 5') | Q(published=True))
 | 
			
		||||
        q = BlogPost.objects(Q(title="Test 5") | Q(published=True))
 | 
			
		||||
        posts = [post.id for post in q]
 | 
			
		||||
 | 
			
		||||
        published_posts = (post2, post3)
 | 
			
		||||
        self.assertTrue(all(obj.id in posts for obj in published_posts))
 | 
			
		||||
 | 
			
		||||
        q = BlogPost.objects(Q(title='Test 1') | Q(published=True))
 | 
			
		||||
        q = BlogPost.objects(Q(title="Test 1") | Q(published=True))
 | 
			
		||||
        posts = [post.id for post in q]
 | 
			
		||||
        published_posts = (post1, post2, post3, post5, post6)
 | 
			
		||||
        self.assertTrue(all(obj.id in posts for obj in published_posts))
 | 
			
		||||
@@ -259,85 +281,91 @@ class QTest(unittest.TestCase):
 | 
			
		||||
        BlogPost.drop_collection()
 | 
			
		||||
 | 
			
		||||
        # Check the 'in' operator
 | 
			
		||||
        self.Person(name='user1', age=20).save()
 | 
			
		||||
        self.Person(name='user2', age=20).save()
 | 
			
		||||
        self.Person(name='user3', age=30).save()
 | 
			
		||||
        self.Person(name='user4', age=40).save()
 | 
			
		||||
        self.Person(name="user1", age=20).save()
 | 
			
		||||
        self.Person(name="user2", age=20).save()
 | 
			
		||||
        self.Person(name="user3", age=30).save()
 | 
			
		||||
        self.Person(name="user4", age=40).save()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(self.Person.objects(Q(age__in=[20])).count(), 2)
 | 
			
		||||
        self.assertEqual(self.Person.objects(Q(age__in=[20, 30])).count(), 3)
 | 
			
		||||
 | 
			
		||||
        # Test invalid query objs
 | 
			
		||||
        with self.assertRaises(InvalidQueryError):
 | 
			
		||||
            self.Person.objects('user1')
 | 
			
		||||
            self.Person.objects("user1")
 | 
			
		||||
 | 
			
		||||
        # filter should fail, too
 | 
			
		||||
        with self.assertRaises(InvalidQueryError):
 | 
			
		||||
            self.Person.objects.filter('user1')
 | 
			
		||||
            self.Person.objects.filter("user1")
 | 
			
		||||
 | 
			
		||||
    def test_q_regex(self):
 | 
			
		||||
        """Ensure that Q objects can be queried using regexes.
 | 
			
		||||
        """
 | 
			
		||||
        person = self.Person(name='Guido van Rossum')
 | 
			
		||||
        person = self.Person(name="Guido van Rossum")
 | 
			
		||||
        person.save()
 | 
			
		||||
 | 
			
		||||
        obj = self.Person.objects(Q(name=re.compile('^Gui'))).first()
 | 
			
		||||
        obj = self.Person.objects(Q(name=re.compile("^Gui"))).first()
 | 
			
		||||
        self.assertEqual(obj, person)
 | 
			
		||||
        obj = self.Person.objects(Q(name=re.compile('^gui'))).first()
 | 
			
		||||
        obj = self.Person.objects(Q(name=re.compile("^gui"))).first()
 | 
			
		||||
        self.assertEqual(obj, None)
 | 
			
		||||
 | 
			
		||||
        obj = self.Person.objects(Q(name=re.compile('^gui', re.I))).first()
 | 
			
		||||
        obj = self.Person.objects(Q(name=re.compile("^gui", re.I))).first()
 | 
			
		||||
        self.assertEqual(obj, person)
 | 
			
		||||
 | 
			
		||||
        obj = self.Person.objects(Q(name__not=re.compile('^bob'))).first()
 | 
			
		||||
        obj = self.Person.objects(Q(name__not=re.compile("^bob"))).first()
 | 
			
		||||
        self.assertEqual(obj, person)
 | 
			
		||||
 | 
			
		||||
        obj = self.Person.objects(Q(name__not=re.compile('^Gui'))).first()
 | 
			
		||||
        obj = self.Person.objects(Q(name__not=re.compile("^Gui"))).first()
 | 
			
		||||
        self.assertEqual(obj, None)
 | 
			
		||||
 | 
			
		||||
    def test_q_repr(self):
 | 
			
		||||
        self.assertEqual(repr(Q()), 'Q(**{})')
 | 
			
		||||
        self.assertEqual(repr(Q(name='test')), "Q(**{'name': 'test'})")
 | 
			
		||||
        self.assertEqual(repr(Q()), "Q(**{})")
 | 
			
		||||
        self.assertEqual(repr(Q(name="test")), "Q(**{'name': 'test'})")
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            repr(Q(name='test') & Q(age__gte=18)),
 | 
			
		||||
            "(Q(**{'name': 'test'}) & Q(**{'age__gte': 18}))")
 | 
			
		||||
            repr(Q(name="test") & Q(age__gte=18)),
 | 
			
		||||
            "(Q(**{'name': 'test'}) & Q(**{'age__gte': 18}))",
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            repr(Q(name='test') | Q(age__gte=18)),
 | 
			
		||||
            "(Q(**{'name': 'test'}) | Q(**{'age__gte': 18}))")
 | 
			
		||||
            repr(Q(name="test") | Q(age__gte=18)),
 | 
			
		||||
            "(Q(**{'name': 'test'}) | Q(**{'age__gte': 18}))",
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_q_lists(self):
 | 
			
		||||
        """Ensure that Q objects query ListFields correctly.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class BlogPost(Document):
 | 
			
		||||
            tags = ListField(StringField())
 | 
			
		||||
 | 
			
		||||
        BlogPost.drop_collection()
 | 
			
		||||
 | 
			
		||||
        BlogPost(tags=['python', 'mongo']).save()
 | 
			
		||||
        BlogPost(tags=['python']).save()
 | 
			
		||||
        BlogPost(tags=["python", "mongo"]).save()
 | 
			
		||||
        BlogPost(tags=["python"]).save()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(BlogPost.objects(Q(tags='mongo')).count(), 1)
 | 
			
		||||
        self.assertEqual(BlogPost.objects(Q(tags='python')).count(), 2)
 | 
			
		||||
        self.assertEqual(BlogPost.objects(Q(tags="mongo")).count(), 1)
 | 
			
		||||
        self.assertEqual(BlogPost.objects(Q(tags="python")).count(), 2)
 | 
			
		||||
 | 
			
		||||
        BlogPost.drop_collection()
 | 
			
		||||
 | 
			
		||||
    def test_q_merge_queries_edge_case(self):
 | 
			
		||||
 | 
			
		||||
        class User(Document):
 | 
			
		||||
            email = EmailField(required=False)
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
        User.drop_collection()
 | 
			
		||||
        pk = ObjectId()
 | 
			
		||||
        User(email='example@example.com', pk=pk).save()
 | 
			
		||||
        User(email="example@example.com", pk=pk).save()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(1, User.objects.filter(Q(email='example@example.com') |
 | 
			
		||||
                                                Q(name='John Doe')).limit(2).filter(pk=pk).count())
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            1,
 | 
			
		||||
            User.objects.filter(Q(email="example@example.com") | Q(name="John Doe"))
 | 
			
		||||
            .limit(2)
 | 
			
		||||
            .filter(pk=pk)
 | 
			
		||||
            .count(),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_chained_q_or_filtering(self):
 | 
			
		||||
 | 
			
		||||
        class Post(EmbeddedDocument):
 | 
			
		||||
            name = StringField(required=True)
 | 
			
		||||
 | 
			
		||||
@@ -350,9 +378,16 @@ class QTest(unittest.TestCase):
 | 
			
		||||
        Item(postables=[Post(name="a"), Post(name="c")]).save()
 | 
			
		||||
        Item(postables=[Post(name="a"), Post(name="b"), Post(name="c")]).save()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(Item.objects(Q(postables__name="a") & Q(postables__name="b")).count(), 2)
 | 
			
		||||
        self.assertEqual(Item.objects.filter(postables__name="a").filter(postables__name="b").count(), 2)
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            Item.objects(Q(postables__name="a") & Q(postables__name="b")).count(), 2
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            Item.objects.filter(postables__name="a")
 | 
			
		||||
            .filter(postables__name="b")
 | 
			
		||||
            .count(),
 | 
			
		||||
            2,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    unittest.main()
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,6 @@ from mongoengine import Document
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestCommon(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    def test__import_class(self):
 | 
			
		||||
        doc_cls = _import_class("Document")
 | 
			
		||||
        self.assertIs(doc_cls, Document)
 | 
			
		||||
 
 | 
			
		||||
@@ -14,12 +14,21 @@ import pymongo
 | 
			
		||||
from bson.tz_util import utc
 | 
			
		||||
 | 
			
		||||
from mongoengine import (
 | 
			
		||||
    connect, register_connection,
 | 
			
		||||
    Document, DateTimeField,
 | 
			
		||||
    disconnect_all, StringField)
 | 
			
		||||
    connect,
 | 
			
		||||
    register_connection,
 | 
			
		||||
    Document,
 | 
			
		||||
    DateTimeField,
 | 
			
		||||
    disconnect_all,
 | 
			
		||||
    StringField,
 | 
			
		||||
)
 | 
			
		||||
import mongoengine.connection
 | 
			
		||||
from mongoengine.connection import (MongoEngineConnectionError, get_db,
 | 
			
		||||
                                    get_connection, disconnect, DEFAULT_DATABASE_NAME)
 | 
			
		||||
from mongoengine.connection import (
 | 
			
		||||
    MongoEngineConnectionError,
 | 
			
		||||
    get_db,
 | 
			
		||||
    get_connection,
 | 
			
		||||
    disconnect,
 | 
			
		||||
    DEFAULT_DATABASE_NAME,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_tz_awareness(connection):
 | 
			
		||||
@@ -27,7 +36,6 @@ def get_tz_awareness(connection):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ConnectionTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def setUpClass(cls):
 | 
			
		||||
        disconnect_all()
 | 
			
		||||
@@ -43,44 +51,46 @@ class ConnectionTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    def test_connect(self):
 | 
			
		||||
        """Ensure that the connect() method works properly."""
 | 
			
		||||
        connect('mongoenginetest')
 | 
			
		||||
        connect("mongoenginetest")
 | 
			
		||||
 | 
			
		||||
        conn = get_connection()
 | 
			
		||||
        self.assertIsInstance(conn, pymongo.mongo_client.MongoClient)
 | 
			
		||||
 | 
			
		||||
        db = get_db()
 | 
			
		||||
        self.assertIsInstance(db, pymongo.database.Database)
 | 
			
		||||
        self.assertEqual(db.name, 'mongoenginetest')
 | 
			
		||||
        self.assertEqual(db.name, "mongoenginetest")
 | 
			
		||||
 | 
			
		||||
        connect('mongoenginetest2', alias='testdb')
 | 
			
		||||
        conn = get_connection('testdb')
 | 
			
		||||
        connect("mongoenginetest2", alias="testdb")
 | 
			
		||||
        conn = get_connection("testdb")
 | 
			
		||||
        self.assertIsInstance(conn, pymongo.mongo_client.MongoClient)
 | 
			
		||||
 | 
			
		||||
    def test_connect_disconnect_works_properly(self):
 | 
			
		||||
        class History1(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            meta = {'db_alias': 'db1'}
 | 
			
		||||
            meta = {"db_alias": "db1"}
 | 
			
		||||
 | 
			
		||||
        class History2(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            meta = {'db_alias': 'db2'}
 | 
			
		||||
            meta = {"db_alias": "db2"}
 | 
			
		||||
 | 
			
		||||
        connect('db1', alias='db1')
 | 
			
		||||
        connect('db2', alias='db2')
 | 
			
		||||
        connect("db1", alias="db1")
 | 
			
		||||
        connect("db2", alias="db2")
 | 
			
		||||
 | 
			
		||||
        History1.drop_collection()
 | 
			
		||||
        History2.drop_collection()
 | 
			
		||||
 | 
			
		||||
        h = History1(name='default').save()
 | 
			
		||||
        h1 = History2(name='db1').save()
 | 
			
		||||
        h = History1(name="default").save()
 | 
			
		||||
        h1 = History2(name="db1").save()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(list(History1.objects().as_pymongo()),
 | 
			
		||||
                         [{'_id': h.id, 'name': 'default'}])
 | 
			
		||||
        self.assertEqual(list(History2.objects().as_pymongo()),
 | 
			
		||||
                         [{'_id': h1.id, 'name': 'db1'}])
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            list(History1.objects().as_pymongo()), [{"_id": h.id, "name": "default"}]
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            list(History2.objects().as_pymongo()), [{"_id": h1.id, "name": "db1"}]
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        disconnect('db1')
 | 
			
		||||
        disconnect('db2')
 | 
			
		||||
        disconnect("db1")
 | 
			
		||||
        disconnect("db2")
 | 
			
		||||
 | 
			
		||||
        with self.assertRaises(MongoEngineConnectionError):
 | 
			
		||||
            list(History1.objects().as_pymongo())
 | 
			
		||||
@@ -88,13 +98,15 @@ class ConnectionTest(unittest.TestCase):
 | 
			
		||||
        with self.assertRaises(MongoEngineConnectionError):
 | 
			
		||||
            list(History2.objects().as_pymongo())
 | 
			
		||||
 | 
			
		||||
        connect('db1', alias='db1')
 | 
			
		||||
        connect('db2', alias='db2')
 | 
			
		||||
        connect("db1", alias="db1")
 | 
			
		||||
        connect("db2", alias="db2")
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(list(History1.objects().as_pymongo()),
 | 
			
		||||
                         [{'_id': h.id, 'name': 'default'}])
 | 
			
		||||
        self.assertEqual(list(History2.objects().as_pymongo()),
 | 
			
		||||
                         [{'_id': h1.id, 'name': 'db1'}])
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            list(History1.objects().as_pymongo()), [{"_id": h.id, "name": "default"}]
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            list(History2.objects().as_pymongo()), [{"_id": h1.id, "name": "db1"}]
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_connect_different_documents_to_different_database(self):
 | 
			
		||||
        class History(Document):
 | 
			
		||||
@@ -102,99 +114,110 @@ class ConnectionTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
        class History1(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            meta = {'db_alias': 'db1'}
 | 
			
		||||
            meta = {"db_alias": "db1"}
 | 
			
		||||
 | 
			
		||||
        class History2(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            meta = {'db_alias': 'db2'}
 | 
			
		||||
            meta = {"db_alias": "db2"}
 | 
			
		||||
 | 
			
		||||
        connect()
 | 
			
		||||
        connect('db1', alias='db1')
 | 
			
		||||
        connect('db2', alias='db2')
 | 
			
		||||
        connect("db1", alias="db1")
 | 
			
		||||
        connect("db2", alias="db2")
 | 
			
		||||
 | 
			
		||||
        History.drop_collection()
 | 
			
		||||
        History1.drop_collection()
 | 
			
		||||
        History2.drop_collection()
 | 
			
		||||
 | 
			
		||||
        h = History(name='default').save()
 | 
			
		||||
        h1 = History1(name='db1').save()
 | 
			
		||||
        h2 = History2(name='db2').save()
 | 
			
		||||
        h = History(name="default").save()
 | 
			
		||||
        h1 = History1(name="db1").save()
 | 
			
		||||
        h2 = History2(name="db2").save()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(History._collection.database.name, DEFAULT_DATABASE_NAME)
 | 
			
		||||
        self.assertEqual(History1._collection.database.name, 'db1')
 | 
			
		||||
        self.assertEqual(History2._collection.database.name, 'db2')
 | 
			
		||||
        self.assertEqual(History1._collection.database.name, "db1")
 | 
			
		||||
        self.assertEqual(History2._collection.database.name, "db2")
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(list(History.objects().as_pymongo()),
 | 
			
		||||
                         [{'_id': h.id, 'name': 'default'}])
 | 
			
		||||
        self.assertEqual(list(History1.objects().as_pymongo()),
 | 
			
		||||
                         [{'_id': h1.id, 'name': 'db1'}])
 | 
			
		||||
        self.assertEqual(list(History2.objects().as_pymongo()),
 | 
			
		||||
                         [{'_id': h2.id, 'name': 'db2'}])
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            list(History.objects().as_pymongo()), [{"_id": h.id, "name": "default"}]
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            list(History1.objects().as_pymongo()), [{"_id": h1.id, "name": "db1"}]
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            list(History2.objects().as_pymongo()), [{"_id": h2.id, "name": "db2"}]
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_connect_fails_if_connect_2_times_with_default_alias(self):
 | 
			
		||||
        connect('mongoenginetest')
 | 
			
		||||
        connect("mongoenginetest")
 | 
			
		||||
 | 
			
		||||
        with self.assertRaises(MongoEngineConnectionError) as ctx_err:
 | 
			
		||||
            connect('mongoenginetest2')
 | 
			
		||||
        self.assertEqual("A different connection with alias `default` was already registered. Use disconnect() first", str(ctx_err.exception))
 | 
			
		||||
            connect("mongoenginetest2")
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            "A different connection with alias `default` was already registered. Use disconnect() first",
 | 
			
		||||
            str(ctx_err.exception),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_connect_fails_if_connect_2_times_with_custom_alias(self):
 | 
			
		||||
        connect('mongoenginetest', alias='alias1')
 | 
			
		||||
        connect("mongoenginetest", alias="alias1")
 | 
			
		||||
 | 
			
		||||
        with self.assertRaises(MongoEngineConnectionError) as ctx_err:
 | 
			
		||||
            connect('mongoenginetest2', alias='alias1')
 | 
			
		||||
            connect("mongoenginetest2", alias="alias1")
 | 
			
		||||
 | 
			
		||||
        self.assertEqual("A different connection with alias `alias1` was already registered. Use disconnect() first", str(ctx_err.exception))
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            "A different connection with alias `alias1` was already registered. Use disconnect() first",
 | 
			
		||||
            str(ctx_err.exception),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_connect_fails_if_similar_connection_settings_arent_defined_the_same_way(self):
 | 
			
		||||
    def test_connect_fails_if_similar_connection_settings_arent_defined_the_same_way(
 | 
			
		||||
        self
 | 
			
		||||
    ):
 | 
			
		||||
        """Intended to keep the detecton function simple but robust"""
 | 
			
		||||
        db_name = 'mongoenginetest'
 | 
			
		||||
        db_alias = 'alias1'
 | 
			
		||||
        connect(db=db_name, alias=db_alias, host='localhost', port=27017)
 | 
			
		||||
        db_name = "mongoenginetest"
 | 
			
		||||
        db_alias = "alias1"
 | 
			
		||||
        connect(db=db_name, alias=db_alias, host="localhost", port=27017)
 | 
			
		||||
 | 
			
		||||
        with self.assertRaises(MongoEngineConnectionError):
 | 
			
		||||
            connect(host='mongodb://localhost:27017/%s' % db_name, alias=db_alias)
 | 
			
		||||
            connect(host="mongodb://localhost:27017/%s" % db_name, alias=db_alias)
 | 
			
		||||
 | 
			
		||||
    def test_connect_passes_silently_connect_multiple_times_with_same_config(self):
 | 
			
		||||
        # test default connection to `test`
 | 
			
		||||
        connect()
 | 
			
		||||
        connect()
 | 
			
		||||
        self.assertEqual(len(mongoengine.connection._connections), 1)
 | 
			
		||||
        connect('test01', alias='test01')
 | 
			
		||||
        connect('test01', alias='test01')
 | 
			
		||||
        connect("test01", alias="test01")
 | 
			
		||||
        connect("test01", alias="test01")
 | 
			
		||||
        self.assertEqual(len(mongoengine.connection._connections), 2)
 | 
			
		||||
        connect(host='mongodb://localhost:27017/mongoenginetest02', alias='test02')
 | 
			
		||||
        connect(host='mongodb://localhost:27017/mongoenginetest02', alias='test02')
 | 
			
		||||
        connect(host="mongodb://localhost:27017/mongoenginetest02", alias="test02")
 | 
			
		||||
        connect(host="mongodb://localhost:27017/mongoenginetest02", alias="test02")
 | 
			
		||||
        self.assertEqual(len(mongoengine.connection._connections), 3)
 | 
			
		||||
 | 
			
		||||
    def test_connect_with_invalid_db_name(self):
 | 
			
		||||
        """Ensure that connect() method fails fast if db name is invalid
 | 
			
		||||
        """
 | 
			
		||||
        with self.assertRaises(InvalidName):
 | 
			
		||||
            connect('mongomock://localhost')
 | 
			
		||||
            connect("mongomock://localhost")
 | 
			
		||||
 | 
			
		||||
    def test_connect_with_db_name_external(self):
 | 
			
		||||
        """Ensure that connect() works if db name is $external
 | 
			
		||||
        """
 | 
			
		||||
        """Ensure that the connect() method works properly."""
 | 
			
		||||
        connect('$external')
 | 
			
		||||
        connect("$external")
 | 
			
		||||
 | 
			
		||||
        conn = get_connection()
 | 
			
		||||
        self.assertIsInstance(conn, pymongo.mongo_client.MongoClient)
 | 
			
		||||
 | 
			
		||||
        db = get_db()
 | 
			
		||||
        self.assertIsInstance(db, pymongo.database.Database)
 | 
			
		||||
        self.assertEqual(db.name, '$external')
 | 
			
		||||
        self.assertEqual(db.name, "$external")
 | 
			
		||||
 | 
			
		||||
        connect('$external', alias='testdb')
 | 
			
		||||
        conn = get_connection('testdb')
 | 
			
		||||
        connect("$external", alias="testdb")
 | 
			
		||||
        conn = get_connection("testdb")
 | 
			
		||||
        self.assertIsInstance(conn, pymongo.mongo_client.MongoClient)
 | 
			
		||||
 | 
			
		||||
    def test_connect_with_invalid_db_name_type(self):
 | 
			
		||||
        """Ensure that connect() method fails fast if db name has invalid type
 | 
			
		||||
        """
 | 
			
		||||
        with self.assertRaises(TypeError):
 | 
			
		||||
            non_string_db_name = ['e. g. list instead of a string']
 | 
			
		||||
            non_string_db_name = ["e. g. list instead of a string"]
 | 
			
		||||
            connect(non_string_db_name)
 | 
			
		||||
 | 
			
		||||
    def test_connect_in_mocking(self):
 | 
			
		||||
@@ -203,34 +226,47 @@ class ConnectionTest(unittest.TestCase):
 | 
			
		||||
        try:
 | 
			
		||||
            import mongomock
 | 
			
		||||
        except ImportError:
 | 
			
		||||
            raise SkipTest('you need mongomock installed to run this testcase')
 | 
			
		||||
            raise SkipTest("you need mongomock installed to run this testcase")
 | 
			
		||||
 | 
			
		||||
        connect('mongoenginetest', host='mongomock://localhost')
 | 
			
		||||
        connect("mongoenginetest", host="mongomock://localhost")
 | 
			
		||||
        conn = get_connection()
 | 
			
		||||
        self.assertIsInstance(conn, mongomock.MongoClient)
 | 
			
		||||
 | 
			
		||||
        connect('mongoenginetest2', host='mongomock://localhost', alias='testdb2')
 | 
			
		||||
        conn = get_connection('testdb2')
 | 
			
		||||
        connect("mongoenginetest2", host="mongomock://localhost", alias="testdb2")
 | 
			
		||||
        conn = get_connection("testdb2")
 | 
			
		||||
        self.assertIsInstance(conn, mongomock.MongoClient)
 | 
			
		||||
 | 
			
		||||
        connect('mongoenginetest3', host='mongodb://localhost', is_mock=True, alias='testdb3')
 | 
			
		||||
        conn = get_connection('testdb3')
 | 
			
		||||
        connect(
 | 
			
		||||
            "mongoenginetest3",
 | 
			
		||||
            host="mongodb://localhost",
 | 
			
		||||
            is_mock=True,
 | 
			
		||||
            alias="testdb3",
 | 
			
		||||
        )
 | 
			
		||||
        conn = get_connection("testdb3")
 | 
			
		||||
        self.assertIsInstance(conn, mongomock.MongoClient)
 | 
			
		||||
 | 
			
		||||
        connect('mongoenginetest4', is_mock=True, alias='testdb4')
 | 
			
		||||
        conn = get_connection('testdb4')
 | 
			
		||||
        connect("mongoenginetest4", is_mock=True, alias="testdb4")
 | 
			
		||||
        conn = get_connection("testdb4")
 | 
			
		||||
        self.assertIsInstance(conn, mongomock.MongoClient)
 | 
			
		||||
 | 
			
		||||
        connect(host='mongodb://localhost:27017/mongoenginetest5', is_mock=True, alias='testdb5')
 | 
			
		||||
        conn = get_connection('testdb5')
 | 
			
		||||
        connect(
 | 
			
		||||
            host="mongodb://localhost:27017/mongoenginetest5",
 | 
			
		||||
            is_mock=True,
 | 
			
		||||
            alias="testdb5",
 | 
			
		||||
        )
 | 
			
		||||
        conn = get_connection("testdb5")
 | 
			
		||||
        self.assertIsInstance(conn, mongomock.MongoClient)
 | 
			
		||||
 | 
			
		||||
        connect(host='mongomock://localhost:27017/mongoenginetest6', alias='testdb6')
 | 
			
		||||
        conn = get_connection('testdb6')
 | 
			
		||||
        connect(host="mongomock://localhost:27017/mongoenginetest6", alias="testdb6")
 | 
			
		||||
        conn = get_connection("testdb6")
 | 
			
		||||
        self.assertIsInstance(conn, mongomock.MongoClient)
 | 
			
		||||
 | 
			
		||||
        connect(host='mongomock://localhost:27017/mongoenginetest7', is_mock=True, alias='testdb7')
 | 
			
		||||
        conn = get_connection('testdb7')
 | 
			
		||||
        connect(
 | 
			
		||||
            host="mongomock://localhost:27017/mongoenginetest7",
 | 
			
		||||
            is_mock=True,
 | 
			
		||||
            alias="testdb7",
 | 
			
		||||
        )
 | 
			
		||||
        conn = get_connection("testdb7")
 | 
			
		||||
        self.assertIsInstance(conn, mongomock.MongoClient)
 | 
			
		||||
 | 
			
		||||
    def test_connect_with_host_list(self):
 | 
			
		||||
@@ -241,30 +277,39 @@ class ConnectionTest(unittest.TestCase):
 | 
			
		||||
        try:
 | 
			
		||||
            import mongomock
 | 
			
		||||
        except ImportError:
 | 
			
		||||
            raise SkipTest('you need mongomock installed to run this testcase')
 | 
			
		||||
            raise SkipTest("you need mongomock installed to run this testcase")
 | 
			
		||||
 | 
			
		||||
        connect(host=['mongomock://localhost'])
 | 
			
		||||
        connect(host=["mongomock://localhost"])
 | 
			
		||||
        conn = get_connection()
 | 
			
		||||
        self.assertIsInstance(conn, mongomock.MongoClient)
 | 
			
		||||
 | 
			
		||||
        connect(host=['mongodb://localhost'], is_mock=True, alias='testdb2')
 | 
			
		||||
        conn = get_connection('testdb2')
 | 
			
		||||
        connect(host=["mongodb://localhost"], is_mock=True, alias="testdb2")
 | 
			
		||||
        conn = get_connection("testdb2")
 | 
			
		||||
        self.assertIsInstance(conn, mongomock.MongoClient)
 | 
			
		||||
 | 
			
		||||
        connect(host=['localhost'], is_mock=True, alias='testdb3')
 | 
			
		||||
        conn = get_connection('testdb3')
 | 
			
		||||
        connect(host=["localhost"], is_mock=True, alias="testdb3")
 | 
			
		||||
        conn = get_connection("testdb3")
 | 
			
		||||
        self.assertIsInstance(conn, mongomock.MongoClient)
 | 
			
		||||
 | 
			
		||||
        connect(host=['mongomock://localhost:27017', 'mongomock://localhost:27018'], alias='testdb4')
 | 
			
		||||
        conn = get_connection('testdb4')
 | 
			
		||||
        connect(
 | 
			
		||||
            host=["mongomock://localhost:27017", "mongomock://localhost:27018"],
 | 
			
		||||
            alias="testdb4",
 | 
			
		||||
        )
 | 
			
		||||
        conn = get_connection("testdb4")
 | 
			
		||||
        self.assertIsInstance(conn, mongomock.MongoClient)
 | 
			
		||||
 | 
			
		||||
        connect(host=['mongodb://localhost:27017', 'mongodb://localhost:27018'], is_mock=True, alias='testdb5')
 | 
			
		||||
        conn = get_connection('testdb5')
 | 
			
		||||
        connect(
 | 
			
		||||
            host=["mongodb://localhost:27017", "mongodb://localhost:27018"],
 | 
			
		||||
            is_mock=True,
 | 
			
		||||
            alias="testdb5",
 | 
			
		||||
        )
 | 
			
		||||
        conn = get_connection("testdb5")
 | 
			
		||||
        self.assertIsInstance(conn, mongomock.MongoClient)
 | 
			
		||||
 | 
			
		||||
        connect(host=['localhost:27017', 'localhost:27018'], is_mock=True, alias='testdb6')
 | 
			
		||||
        conn = get_connection('testdb6')
 | 
			
		||||
        connect(
 | 
			
		||||
            host=["localhost:27017", "localhost:27018"], is_mock=True, alias="testdb6"
 | 
			
		||||
        )
 | 
			
		||||
        conn = get_connection("testdb6")
 | 
			
		||||
        self.assertIsInstance(conn, mongomock.MongoClient)
 | 
			
		||||
 | 
			
		||||
    def test_disconnect_cleans_globals(self):
 | 
			
		||||
@@ -273,7 +318,7 @@ class ConnectionTest(unittest.TestCase):
 | 
			
		||||
        dbs = mongoengine.connection._dbs
 | 
			
		||||
        connection_settings = mongoengine.connection._connection_settings
 | 
			
		||||
 | 
			
		||||
        connect('mongoenginetest')
 | 
			
		||||
        connect("mongoenginetest")
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(len(connections), 1)
 | 
			
		||||
        self.assertEqual(len(dbs), 0)
 | 
			
		||||
@@ -292,7 +337,7 @@ class ConnectionTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    def test_disconnect_cleans_cached_collection_attribute_in_document(self):
 | 
			
		||||
        """Ensure that the disconnect() method works properly"""
 | 
			
		||||
        conn1 = connect('mongoenginetest')
 | 
			
		||||
        conn1 = connect("mongoenginetest")
 | 
			
		||||
 | 
			
		||||
        class History(Document):
 | 
			
		||||
            pass
 | 
			
		||||
@@ -301,7 +346,7 @@ class ConnectionTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
        History.drop_collection()
 | 
			
		||||
 | 
			
		||||
        History.objects.first()     # will trigger the caching of _collection attribute
 | 
			
		||||
        History.objects.first()  # will trigger the caching of _collection attribute
 | 
			
		||||
        self.assertIsNotNone(History._collection)
 | 
			
		||||
 | 
			
		||||
        disconnect()
 | 
			
		||||
@@ -310,15 +355,17 @@ class ConnectionTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
        with self.assertRaises(MongoEngineConnectionError) as ctx_err:
 | 
			
		||||
            History.objects.first()
 | 
			
		||||
        self.assertEqual("You have not defined a default connection", str(ctx_err.exception))
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            "You have not defined a default connection", str(ctx_err.exception)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_connect_disconnect_works_on_same_document(self):
 | 
			
		||||
        """Ensure that the connect/disconnect works properly with a single Document"""
 | 
			
		||||
        db1 = 'db1'
 | 
			
		||||
        db2 = 'db2'
 | 
			
		||||
        db1 = "db1"
 | 
			
		||||
        db2 = "db2"
 | 
			
		||||
 | 
			
		||||
        # Ensure freshness of the 2 databases through pymongo
 | 
			
		||||
        client = MongoClient('localhost', 27017)
 | 
			
		||||
        client = MongoClient("localhost", 27017)
 | 
			
		||||
        client.drop_database(db1)
 | 
			
		||||
        client.drop_database(db2)
 | 
			
		||||
 | 
			
		||||
@@ -328,44 +375,44 @@ class ConnectionTest(unittest.TestCase):
 | 
			
		||||
        class User(Document):
 | 
			
		||||
            name = StringField(required=True)
 | 
			
		||||
 | 
			
		||||
        user1 = User(name='John is in db1').save()
 | 
			
		||||
        user1 = User(name="John is in db1").save()
 | 
			
		||||
        disconnect()
 | 
			
		||||
 | 
			
		||||
        # Make sure save doesnt work at this stage
 | 
			
		||||
        with self.assertRaises(MongoEngineConnectionError):
 | 
			
		||||
            User(name='Wont work').save()
 | 
			
		||||
            User(name="Wont work").save()
 | 
			
		||||
 | 
			
		||||
        # Save in db2
 | 
			
		||||
        connect(db2)
 | 
			
		||||
        user2 = User(name='Bob is in db2').save()
 | 
			
		||||
        user2 = User(name="Bob is in db2").save()
 | 
			
		||||
        disconnect()
 | 
			
		||||
 | 
			
		||||
        db1_users = list(client[db1].user.find())
 | 
			
		||||
        self.assertEqual(db1_users, [{'_id': user1.id, 'name': 'John is in db1'}])
 | 
			
		||||
        self.assertEqual(db1_users, [{"_id": user1.id, "name": "John is in db1"}])
 | 
			
		||||
        db2_users = list(client[db2].user.find())
 | 
			
		||||
        self.assertEqual(db2_users, [{'_id': user2.id, 'name': 'Bob is in db2'}])
 | 
			
		||||
        self.assertEqual(db2_users, [{"_id": user2.id, "name": "Bob is in db2"}])
 | 
			
		||||
 | 
			
		||||
    def test_disconnect_silently_pass_if_alias_does_not_exist(self):
 | 
			
		||||
        connections = mongoengine.connection._connections
 | 
			
		||||
        self.assertEqual(len(connections), 0)
 | 
			
		||||
        disconnect(alias='not_exist')
 | 
			
		||||
        disconnect(alias="not_exist")
 | 
			
		||||
 | 
			
		||||
    def test_disconnect_all(self):
 | 
			
		||||
        connections = mongoengine.connection._connections
 | 
			
		||||
        dbs = mongoengine.connection._dbs
 | 
			
		||||
        connection_settings = mongoengine.connection._connection_settings
 | 
			
		||||
 | 
			
		||||
        connect('mongoenginetest')
 | 
			
		||||
        connect('mongoenginetest2', alias='db1')
 | 
			
		||||
        connect("mongoenginetest")
 | 
			
		||||
        connect("mongoenginetest2", alias="db1")
 | 
			
		||||
 | 
			
		||||
        class History(Document):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        class History1(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            meta = {'db_alias': 'db1'}
 | 
			
		||||
            meta = {"db_alias": "db1"}
 | 
			
		||||
 | 
			
		||||
        History.drop_collection()   # will trigger the caching of _collection attribute
 | 
			
		||||
        History.drop_collection()  # will trigger the caching of _collection attribute
 | 
			
		||||
        History.objects.first()
 | 
			
		||||
        History1.drop_collection()
 | 
			
		||||
        History1.objects.first()
 | 
			
		||||
@@ -398,11 +445,11 @@ class ConnectionTest(unittest.TestCase):
 | 
			
		||||
    def test_sharing_connections(self):
 | 
			
		||||
        """Ensure that connections are shared when the connection settings are exactly the same
 | 
			
		||||
        """
 | 
			
		||||
        connect('mongoenginetests', alias='testdb1')
 | 
			
		||||
        expected_connection = get_connection('testdb1')
 | 
			
		||||
        connect("mongoenginetests", alias="testdb1")
 | 
			
		||||
        expected_connection = get_connection("testdb1")
 | 
			
		||||
 | 
			
		||||
        connect('mongoenginetests', alias='testdb2')
 | 
			
		||||
        actual_connection = get_connection('testdb2')
 | 
			
		||||
        connect("mongoenginetests", alias="testdb2")
 | 
			
		||||
        actual_connection = get_connection("testdb2")
 | 
			
		||||
 | 
			
		||||
        expected_connection.server_info()
 | 
			
		||||
 | 
			
		||||
@@ -410,7 +457,7 @@ class ConnectionTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    def test_connect_uri(self):
 | 
			
		||||
        """Ensure that the connect() method works properly with URIs."""
 | 
			
		||||
        c = connect(db='mongoenginetest', alias='admin')
 | 
			
		||||
        c = connect(db="mongoenginetest", alias="admin")
 | 
			
		||||
        c.admin.system.users.delete_many({})
 | 
			
		||||
        c.mongoenginetest.system.users.delete_many({})
 | 
			
		||||
 | 
			
		||||
@@ -418,14 +465,16 @@ class ConnectionTest(unittest.TestCase):
 | 
			
		||||
        c.admin.authenticate("admin", "password")
 | 
			
		||||
        c.admin.command("createUser", "username", pwd="password", roles=["dbOwner"])
 | 
			
		||||
 | 
			
		||||
        connect("testdb_uri", host='mongodb://username:password@localhost/mongoenginetest')
 | 
			
		||||
        connect(
 | 
			
		||||
            "testdb_uri", host="mongodb://username:password@localhost/mongoenginetest"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        conn = get_connection()
 | 
			
		||||
        self.assertIsInstance(conn, pymongo.mongo_client.MongoClient)
 | 
			
		||||
 | 
			
		||||
        db = get_db()
 | 
			
		||||
        self.assertIsInstance(db, pymongo.database.Database)
 | 
			
		||||
        self.assertEqual(db.name, 'mongoenginetest')
 | 
			
		||||
        self.assertEqual(db.name, "mongoenginetest")
 | 
			
		||||
 | 
			
		||||
        c.admin.system.users.delete_many({})
 | 
			
		||||
        c.mongoenginetest.system.users.delete_many({})
 | 
			
		||||
@@ -434,35 +483,35 @@ class ConnectionTest(unittest.TestCase):
 | 
			
		||||
        """Ensure connect() method works properly if the URI doesn't
 | 
			
		||||
        include a database name.
 | 
			
		||||
        """
 | 
			
		||||
        connect("mongoenginetest", host='mongodb://localhost/')
 | 
			
		||||
        connect("mongoenginetest", host="mongodb://localhost/")
 | 
			
		||||
 | 
			
		||||
        conn = get_connection()
 | 
			
		||||
        self.assertIsInstance(conn, pymongo.mongo_client.MongoClient)
 | 
			
		||||
 | 
			
		||||
        db = get_db()
 | 
			
		||||
        self.assertIsInstance(db, pymongo.database.Database)
 | 
			
		||||
        self.assertEqual(db.name, 'mongoenginetest')
 | 
			
		||||
        self.assertEqual(db.name, "mongoenginetest")
 | 
			
		||||
 | 
			
		||||
    def test_connect_uri_default_db(self):
 | 
			
		||||
        """Ensure connect() defaults to the right database name if
 | 
			
		||||
        the URI and the database_name don't explicitly specify it.
 | 
			
		||||
        """
 | 
			
		||||
        connect(host='mongodb://localhost/')
 | 
			
		||||
        connect(host="mongodb://localhost/")
 | 
			
		||||
 | 
			
		||||
        conn = get_connection()
 | 
			
		||||
        self.assertIsInstance(conn, pymongo.mongo_client.MongoClient)
 | 
			
		||||
 | 
			
		||||
        db = get_db()
 | 
			
		||||
        self.assertIsInstance(db, pymongo.database.Database)
 | 
			
		||||
        self.assertEqual(db.name, 'test')
 | 
			
		||||
        self.assertEqual(db.name, "test")
 | 
			
		||||
 | 
			
		||||
    def test_uri_without_credentials_doesnt_override_conn_settings(self):
 | 
			
		||||
        """Ensure connect() uses the username & password params if the URI
 | 
			
		||||
        doesn't explicitly specify them.
 | 
			
		||||
        """
 | 
			
		||||
        c = connect(host='mongodb://localhost/mongoenginetest',
 | 
			
		||||
                    username='user',
 | 
			
		||||
                    password='pass')
 | 
			
		||||
        c = connect(
 | 
			
		||||
            host="mongodb://localhost/mongoenginetest", username="user", password="pass"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # OperationFailure means that mongoengine attempted authentication
 | 
			
		||||
        # w/ the provided username/password and failed - that's the desired
 | 
			
		||||
@@ -474,27 +523,31 @@ class ConnectionTest(unittest.TestCase):
 | 
			
		||||
        option in the URI.
 | 
			
		||||
        """
 | 
			
		||||
        # Create users
 | 
			
		||||
        c = connect('mongoenginetest')
 | 
			
		||||
        c = connect("mongoenginetest")
 | 
			
		||||
 | 
			
		||||
        c.admin.system.users.delete_many({})
 | 
			
		||||
        c.admin.command("createUser", "username2", pwd="password", roles=["dbOwner"])
 | 
			
		||||
 | 
			
		||||
        # Authentication fails without "authSource"
 | 
			
		||||
        test_conn = connect(
 | 
			
		||||
            'mongoenginetest', alias='test1',
 | 
			
		||||
            host='mongodb://username2:password@localhost/mongoenginetest'
 | 
			
		||||
            "mongoenginetest",
 | 
			
		||||
            alias="test1",
 | 
			
		||||
            host="mongodb://username2:password@localhost/mongoenginetest",
 | 
			
		||||
        )
 | 
			
		||||
        self.assertRaises(OperationFailure, test_conn.server_info)
 | 
			
		||||
 | 
			
		||||
        # Authentication succeeds with "authSource"
 | 
			
		||||
        authd_conn = connect(
 | 
			
		||||
            'mongoenginetest', alias='test2',
 | 
			
		||||
            host=('mongodb://username2:password@localhost/'
 | 
			
		||||
                  'mongoenginetest?authSource=admin')
 | 
			
		||||
            "mongoenginetest",
 | 
			
		||||
            alias="test2",
 | 
			
		||||
            host=(
 | 
			
		||||
                "mongodb://username2:password@localhost/"
 | 
			
		||||
                "mongoenginetest?authSource=admin"
 | 
			
		||||
            ),
 | 
			
		||||
        )
 | 
			
		||||
        db = get_db('test2')
 | 
			
		||||
        db = get_db("test2")
 | 
			
		||||
        self.assertIsInstance(db, pymongo.database.Database)
 | 
			
		||||
        self.assertEqual(db.name, 'mongoenginetest')
 | 
			
		||||
        self.assertEqual(db.name, "mongoenginetest")
 | 
			
		||||
 | 
			
		||||
        # Clear all users
 | 
			
		||||
        authd_conn.admin.system.users.delete_many({})
 | 
			
		||||
@@ -502,82 +555,86 @@ class ConnectionTest(unittest.TestCase):
 | 
			
		||||
    def test_register_connection(self):
 | 
			
		||||
        """Ensure that connections with different aliases may be registered.
 | 
			
		||||
        """
 | 
			
		||||
        register_connection('testdb', 'mongoenginetest2')
 | 
			
		||||
        register_connection("testdb", "mongoenginetest2")
 | 
			
		||||
 | 
			
		||||
        self.assertRaises(MongoEngineConnectionError, get_connection)
 | 
			
		||||
        conn = get_connection('testdb')
 | 
			
		||||
        conn = get_connection("testdb")
 | 
			
		||||
        self.assertIsInstance(conn, pymongo.mongo_client.MongoClient)
 | 
			
		||||
 | 
			
		||||
        db = get_db('testdb')
 | 
			
		||||
        db = get_db("testdb")
 | 
			
		||||
        self.assertIsInstance(db, pymongo.database.Database)
 | 
			
		||||
        self.assertEqual(db.name, 'mongoenginetest2')
 | 
			
		||||
        self.assertEqual(db.name, "mongoenginetest2")
 | 
			
		||||
 | 
			
		||||
    def test_register_connection_defaults(self):
 | 
			
		||||
        """Ensure that defaults are used when the host and port are None.
 | 
			
		||||
        """
 | 
			
		||||
        register_connection('testdb', 'mongoenginetest', host=None, port=None)
 | 
			
		||||
        register_connection("testdb", "mongoenginetest", host=None, port=None)
 | 
			
		||||
 | 
			
		||||
        conn = get_connection('testdb')
 | 
			
		||||
        conn = get_connection("testdb")
 | 
			
		||||
        self.assertIsInstance(conn, pymongo.mongo_client.MongoClient)
 | 
			
		||||
 | 
			
		||||
    def test_connection_kwargs(self):
 | 
			
		||||
        """Ensure that connection kwargs get passed to pymongo."""
 | 
			
		||||
        connect('mongoenginetest', alias='t1', tz_aware=True)
 | 
			
		||||
        conn = get_connection('t1')
 | 
			
		||||
        connect("mongoenginetest", alias="t1", tz_aware=True)
 | 
			
		||||
        conn = get_connection("t1")
 | 
			
		||||
 | 
			
		||||
        self.assertTrue(get_tz_awareness(conn))
 | 
			
		||||
 | 
			
		||||
        connect('mongoenginetest2', alias='t2')
 | 
			
		||||
        conn = get_connection('t2')
 | 
			
		||||
        connect("mongoenginetest2", alias="t2")
 | 
			
		||||
        conn = get_connection("t2")
 | 
			
		||||
        self.assertFalse(get_tz_awareness(conn))
 | 
			
		||||
 | 
			
		||||
    def test_connection_pool_via_kwarg(self):
 | 
			
		||||
        """Ensure we can specify a max connection pool size using
 | 
			
		||||
        a connection kwarg.
 | 
			
		||||
        """
 | 
			
		||||
        pool_size_kwargs = {'maxpoolsize': 100}
 | 
			
		||||
        pool_size_kwargs = {"maxpoolsize": 100}
 | 
			
		||||
 | 
			
		||||
        conn = connect('mongoenginetest', alias='max_pool_size_via_kwarg', **pool_size_kwargs)
 | 
			
		||||
        conn = connect(
 | 
			
		||||
            "mongoenginetest", alias="max_pool_size_via_kwarg", **pool_size_kwargs
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(conn.max_pool_size, 100)
 | 
			
		||||
 | 
			
		||||
    def test_connection_pool_via_uri(self):
 | 
			
		||||
        """Ensure we can specify a max connection pool size using
 | 
			
		||||
        an option in a connection URI.
 | 
			
		||||
        """
 | 
			
		||||
        conn = connect(host='mongodb://localhost/test?maxpoolsize=100', alias='max_pool_size_via_uri')
 | 
			
		||||
        conn = connect(
 | 
			
		||||
            host="mongodb://localhost/test?maxpoolsize=100",
 | 
			
		||||
            alias="max_pool_size_via_uri",
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(conn.max_pool_size, 100)
 | 
			
		||||
 | 
			
		||||
    def test_write_concern(self):
 | 
			
		||||
        """Ensure write concern can be specified in connect() via
 | 
			
		||||
        a kwarg or as part of the connection URI.
 | 
			
		||||
        """
 | 
			
		||||
        conn1 = connect(alias='conn1', host='mongodb://localhost/testing?w=1&j=true')
 | 
			
		||||
        conn2 = connect('testing', alias='conn2', w=1, j=True)
 | 
			
		||||
        self.assertEqual(conn1.write_concern.document, {'w': 1, 'j': True})
 | 
			
		||||
        self.assertEqual(conn2.write_concern.document, {'w': 1, 'j': True})
 | 
			
		||||
        conn1 = connect(alias="conn1", host="mongodb://localhost/testing?w=1&j=true")
 | 
			
		||||
        conn2 = connect("testing", alias="conn2", w=1, j=True)
 | 
			
		||||
        self.assertEqual(conn1.write_concern.document, {"w": 1, "j": True})
 | 
			
		||||
        self.assertEqual(conn2.write_concern.document, {"w": 1, "j": True})
 | 
			
		||||
 | 
			
		||||
    def test_connect_with_replicaset_via_uri(self):
 | 
			
		||||
        """Ensure connect() works when specifying a replicaSet via the
 | 
			
		||||
        MongoDB URI.
 | 
			
		||||
        """
 | 
			
		||||
        c = connect(host='mongodb://localhost/test?replicaSet=local-rs')
 | 
			
		||||
        c = connect(host="mongodb://localhost/test?replicaSet=local-rs")
 | 
			
		||||
        db = get_db()
 | 
			
		||||
        self.assertIsInstance(db, pymongo.database.Database)
 | 
			
		||||
        self.assertEqual(db.name, 'test')
 | 
			
		||||
        self.assertEqual(db.name, "test")
 | 
			
		||||
 | 
			
		||||
    def test_connect_with_replicaset_via_kwargs(self):
 | 
			
		||||
        """Ensure connect() works when specifying a replicaSet via the
 | 
			
		||||
        connection kwargs
 | 
			
		||||
        """
 | 
			
		||||
        c = connect(replicaset='local-rs')
 | 
			
		||||
        self.assertEqual(c._MongoClient__options.replica_set_name,
 | 
			
		||||
                         'local-rs')
 | 
			
		||||
        c = connect(replicaset="local-rs")
 | 
			
		||||
        self.assertEqual(c._MongoClient__options.replica_set_name, "local-rs")
 | 
			
		||||
        db = get_db()
 | 
			
		||||
        self.assertIsInstance(db, pymongo.database.Database)
 | 
			
		||||
        self.assertEqual(db.name, 'test')
 | 
			
		||||
        self.assertEqual(db.name, "test")
 | 
			
		||||
 | 
			
		||||
    def test_connect_tz_aware(self):
 | 
			
		||||
        connect('mongoenginetest', tz_aware=True)
 | 
			
		||||
        connect("mongoenginetest", tz_aware=True)
 | 
			
		||||
        d = datetime.datetime(2010, 5, 5, tzinfo=utc)
 | 
			
		||||
 | 
			
		||||
        class DateDoc(Document):
 | 
			
		||||
@@ -590,37 +647,39 @@ class ConnectionTest(unittest.TestCase):
 | 
			
		||||
        self.assertEqual(d, date_doc.the_date)
 | 
			
		||||
 | 
			
		||||
    def test_read_preference_from_parse(self):
 | 
			
		||||
        conn = connect(host="mongodb://a1.vpc,a2.vpc,a3.vpc/prod?readPreference=secondaryPreferred")
 | 
			
		||||
        conn = connect(
 | 
			
		||||
            host="mongodb://a1.vpc,a2.vpc,a3.vpc/prod?readPreference=secondaryPreferred"
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(conn.read_preference, ReadPreference.SECONDARY_PREFERRED)
 | 
			
		||||
 | 
			
		||||
    def test_multiple_connection_settings(self):
 | 
			
		||||
        connect('mongoenginetest', alias='t1', host="localhost")
 | 
			
		||||
        connect("mongoenginetest", alias="t1", host="localhost")
 | 
			
		||||
 | 
			
		||||
        connect('mongoenginetest2', alias='t2', host="127.0.0.1")
 | 
			
		||||
        connect("mongoenginetest2", alias="t2", host="127.0.0.1")
 | 
			
		||||
 | 
			
		||||
        mongo_connections = mongoengine.connection._connections
 | 
			
		||||
        self.assertEqual(len(mongo_connections.items()), 2)
 | 
			
		||||
        self.assertIn('t1', mongo_connections.keys())
 | 
			
		||||
        self.assertIn('t2', mongo_connections.keys())
 | 
			
		||||
        self.assertIn("t1", mongo_connections.keys())
 | 
			
		||||
        self.assertIn("t2", mongo_connections.keys())
 | 
			
		||||
 | 
			
		||||
        # Handle PyMongo 3+ Async Connection
 | 
			
		||||
        # Ensure we are connected, throws ServerSelectionTimeoutError otherwise.
 | 
			
		||||
        # Purposely not catching exception to fail test if thrown.
 | 
			
		||||
        mongo_connections['t1'].server_info()
 | 
			
		||||
        mongo_connections['t2'].server_info()
 | 
			
		||||
        self.assertEqual(mongo_connections['t1'].address[0], 'localhost')
 | 
			
		||||
        self.assertEqual(mongo_connections['t2'].address[0], '127.0.0.1')
 | 
			
		||||
        mongo_connections["t1"].server_info()
 | 
			
		||||
        mongo_connections["t2"].server_info()
 | 
			
		||||
        self.assertEqual(mongo_connections["t1"].address[0], "localhost")
 | 
			
		||||
        self.assertEqual(mongo_connections["t2"].address[0], "127.0.0.1")
 | 
			
		||||
 | 
			
		||||
    def test_connect_2_databases_uses_same_client_if_only_dbname_differs(self):
 | 
			
		||||
        c1 = connect(alias='testdb1', db='testdb1')
 | 
			
		||||
        c2 = connect(alias='testdb2', db='testdb2')
 | 
			
		||||
        c1 = connect(alias="testdb1", db="testdb1")
 | 
			
		||||
        c2 = connect(alias="testdb2", db="testdb2")
 | 
			
		||||
        self.assertIs(c1, c2)
 | 
			
		||||
 | 
			
		||||
    def test_connect_2_databases_uses_different_client_if_different_parameters(self):
 | 
			
		||||
        c1 = connect(alias='testdb1', db='testdb1', username='u1')
 | 
			
		||||
        c2 = connect(alias='testdb2', db='testdb2', username='u2')
 | 
			
		||||
        c1 = connect(alias="testdb1", db="testdb1", username="u1")
 | 
			
		||||
        c2 = connect(alias="testdb2", db="testdb2", username="u2")
 | 
			
		||||
        self.assertIsNot(c1, c2)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    unittest.main()
 | 
			
		||||
 
 | 
			
		||||
@@ -2,17 +2,20 @@ import unittest
 | 
			
		||||
 | 
			
		||||
from mongoengine import *
 | 
			
		||||
from mongoengine.connection import get_db
 | 
			
		||||
from mongoengine.context_managers import (switch_db, switch_collection,
 | 
			
		||||
                                          no_sub_classes, no_dereference,
 | 
			
		||||
                                          query_counter)
 | 
			
		||||
from mongoengine.context_managers import (
 | 
			
		||||
    switch_db,
 | 
			
		||||
    switch_collection,
 | 
			
		||||
    no_sub_classes,
 | 
			
		||||
    no_dereference,
 | 
			
		||||
    query_counter,
 | 
			
		||||
)
 | 
			
		||||
from mongoengine.pymongo_support import count_documents
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ContextManagersTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    def test_switch_db_context_manager(self):
 | 
			
		||||
        connect('mongoenginetest')
 | 
			
		||||
        register_connection('testdb-1', 'mongoenginetest2')
 | 
			
		||||
        connect("mongoenginetest")
 | 
			
		||||
        register_connection("testdb-1", "mongoenginetest2")
 | 
			
		||||
 | 
			
		||||
        class Group(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
@@ -22,7 +25,7 @@ class ContextManagersTest(unittest.TestCase):
 | 
			
		||||
        Group(name="hello - default").save()
 | 
			
		||||
        self.assertEqual(1, Group.objects.count())
 | 
			
		||||
 | 
			
		||||
        with switch_db(Group, 'testdb-1') as Group:
 | 
			
		||||
        with switch_db(Group, "testdb-1") as Group:
 | 
			
		||||
 | 
			
		||||
            self.assertEqual(0, Group.objects.count())
 | 
			
		||||
 | 
			
		||||
@@ -36,21 +39,21 @@ class ContextManagersTest(unittest.TestCase):
 | 
			
		||||
        self.assertEqual(1, Group.objects.count())
 | 
			
		||||
 | 
			
		||||
    def test_switch_collection_context_manager(self):
 | 
			
		||||
        connect('mongoenginetest')
 | 
			
		||||
        register_connection(alias='testdb-1', db='mongoenginetest2')
 | 
			
		||||
        connect("mongoenginetest")
 | 
			
		||||
        register_connection(alias="testdb-1", db="mongoenginetest2")
 | 
			
		||||
 | 
			
		||||
        class Group(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
        Group.drop_collection()         # drops in default
 | 
			
		||||
        Group.drop_collection()  # drops in default
 | 
			
		||||
 | 
			
		||||
        with switch_collection(Group, 'group1') as Group:
 | 
			
		||||
            Group.drop_collection()     # drops in group1
 | 
			
		||||
        with switch_collection(Group, "group1") as Group:
 | 
			
		||||
            Group.drop_collection()  # drops in group1
 | 
			
		||||
 | 
			
		||||
        Group(name="hello - group").save()
 | 
			
		||||
        self.assertEqual(1, Group.objects.count())
 | 
			
		||||
 | 
			
		||||
        with switch_collection(Group, 'group1') as Group:
 | 
			
		||||
        with switch_collection(Group, "group1") as Group:
 | 
			
		||||
 | 
			
		||||
            self.assertEqual(0, Group.objects.count())
 | 
			
		||||
 | 
			
		||||
@@ -66,7 +69,7 @@ class ContextManagersTest(unittest.TestCase):
 | 
			
		||||
    def test_no_dereference_context_manager_object_id(self):
 | 
			
		||||
        """Ensure that DBRef items in ListFields aren't dereferenced.
 | 
			
		||||
        """
 | 
			
		||||
        connect('mongoenginetest')
 | 
			
		||||
        connect("mongoenginetest")
 | 
			
		||||
 | 
			
		||||
        class User(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
@@ -80,14 +83,14 @@ class ContextManagersTest(unittest.TestCase):
 | 
			
		||||
        Group.drop_collection()
 | 
			
		||||
 | 
			
		||||
        for i in range(1, 51):
 | 
			
		||||
            User(name='user %s' % i).save()
 | 
			
		||||
            User(name="user %s" % i).save()
 | 
			
		||||
 | 
			
		||||
        user = User.objects.first()
 | 
			
		||||
        Group(ref=user, members=User.objects, generic=user).save()
 | 
			
		||||
 | 
			
		||||
        with no_dereference(Group) as NoDeRefGroup:
 | 
			
		||||
            self.assertTrue(Group._fields['members']._auto_dereference)
 | 
			
		||||
            self.assertFalse(NoDeRefGroup._fields['members']._auto_dereference)
 | 
			
		||||
            self.assertTrue(Group._fields["members"]._auto_dereference)
 | 
			
		||||
            self.assertFalse(NoDeRefGroup._fields["members"]._auto_dereference)
 | 
			
		||||
 | 
			
		||||
        with no_dereference(Group) as Group:
 | 
			
		||||
            group = Group.objects.first()
 | 
			
		||||
@@ -104,7 +107,7 @@ class ContextManagersTest(unittest.TestCase):
 | 
			
		||||
    def test_no_dereference_context_manager_dbref(self):
 | 
			
		||||
        """Ensure that DBRef items in ListFields aren't dereferenced.
 | 
			
		||||
        """
 | 
			
		||||
        connect('mongoenginetest')
 | 
			
		||||
        connect("mongoenginetest")
 | 
			
		||||
 | 
			
		||||
        class User(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
@@ -118,31 +121,29 @@ class ContextManagersTest(unittest.TestCase):
 | 
			
		||||
        Group.drop_collection()
 | 
			
		||||
 | 
			
		||||
        for i in range(1, 51):
 | 
			
		||||
            User(name='user %s' % i).save()
 | 
			
		||||
            User(name="user %s" % i).save()
 | 
			
		||||
 | 
			
		||||
        user = User.objects.first()
 | 
			
		||||
        Group(ref=user, members=User.objects, generic=user).save()
 | 
			
		||||
 | 
			
		||||
        with no_dereference(Group) as NoDeRefGroup:
 | 
			
		||||
            self.assertTrue(Group._fields['members']._auto_dereference)
 | 
			
		||||
            self.assertFalse(NoDeRefGroup._fields['members']._auto_dereference)
 | 
			
		||||
            self.assertTrue(Group._fields["members"]._auto_dereference)
 | 
			
		||||
            self.assertFalse(NoDeRefGroup._fields["members"]._auto_dereference)
 | 
			
		||||
 | 
			
		||||
        with no_dereference(Group) as Group:
 | 
			
		||||
            group = Group.objects.first()
 | 
			
		||||
            self.assertTrue(all([not isinstance(m, User)
 | 
			
		||||
                                for m in group.members]))
 | 
			
		||||
            self.assertTrue(all([not isinstance(m, User) for m in group.members]))
 | 
			
		||||
            self.assertNotIsInstance(group.ref, User)
 | 
			
		||||
            self.assertNotIsInstance(group.generic, User)
 | 
			
		||||
 | 
			
		||||
        self.assertTrue(all([isinstance(m, User)
 | 
			
		||||
                             for m in group.members]))
 | 
			
		||||
        self.assertTrue(all([isinstance(m, User) for m in group.members]))
 | 
			
		||||
        self.assertIsInstance(group.ref, User)
 | 
			
		||||
        self.assertIsInstance(group.generic, User)
 | 
			
		||||
 | 
			
		||||
    def test_no_sub_classes(self):
 | 
			
		||||
        class A(Document):
 | 
			
		||||
            x = IntField()
 | 
			
		||||
            meta = {'allow_inheritance': True}
 | 
			
		||||
            meta = {"allow_inheritance": True}
 | 
			
		||||
 | 
			
		||||
        class B(A):
 | 
			
		||||
            z = IntField()
 | 
			
		||||
@@ -188,20 +189,20 @@ class ContextManagersTest(unittest.TestCase):
 | 
			
		||||
    def test_no_sub_classes_modification_to_document_class_are_temporary(self):
 | 
			
		||||
        class A(Document):
 | 
			
		||||
            x = IntField()
 | 
			
		||||
            meta = {'allow_inheritance': True}
 | 
			
		||||
            meta = {"allow_inheritance": True}
 | 
			
		||||
 | 
			
		||||
        class B(A):
 | 
			
		||||
            z = IntField()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(A._subclasses, ('A', 'A.B'))
 | 
			
		||||
        self.assertEqual(A._subclasses, ("A", "A.B"))
 | 
			
		||||
        with no_sub_classes(A):
 | 
			
		||||
            self.assertEqual(A._subclasses, ('A',))
 | 
			
		||||
        self.assertEqual(A._subclasses, ('A', 'A.B'))
 | 
			
		||||
            self.assertEqual(A._subclasses, ("A",))
 | 
			
		||||
        self.assertEqual(A._subclasses, ("A", "A.B"))
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(B._subclasses, ('A.B',))
 | 
			
		||||
        self.assertEqual(B._subclasses, ("A.B",))
 | 
			
		||||
        with no_sub_classes(B):
 | 
			
		||||
            self.assertEqual(B._subclasses, ('A.B',))
 | 
			
		||||
        self.assertEqual(B._subclasses, ('A.B',))
 | 
			
		||||
            self.assertEqual(B._subclasses, ("A.B",))
 | 
			
		||||
        self.assertEqual(B._subclasses, ("A.B",))
 | 
			
		||||
 | 
			
		||||
    def test_no_subclass_context_manager_does_not_swallow_exception(self):
 | 
			
		||||
        class User(Document):
 | 
			
		||||
@@ -218,7 +219,7 @@ class ContextManagersTest(unittest.TestCase):
 | 
			
		||||
                raise TypeError()
 | 
			
		||||
 | 
			
		||||
    def test_query_counter_temporarily_modifies_profiling_level(self):
 | 
			
		||||
        connect('mongoenginetest')
 | 
			
		||||
        connect("mongoenginetest")
 | 
			
		||||
        db = get_db()
 | 
			
		||||
 | 
			
		||||
        initial_profiling_level = db.profiling_level()
 | 
			
		||||
@@ -231,11 +232,13 @@ class ContextManagersTest(unittest.TestCase):
 | 
			
		||||
                self.assertEqual(db.profiling_level(), 2)
 | 
			
		||||
            self.assertEqual(db.profiling_level(), NEW_LEVEL)
 | 
			
		||||
        except Exception:
 | 
			
		||||
            db.set_profiling_level(initial_profiling_level)    # Ensures it gets reseted no matter the outcome of the test
 | 
			
		||||
            db.set_profiling_level(
 | 
			
		||||
                initial_profiling_level
 | 
			
		||||
            )  # Ensures it gets reseted no matter the outcome of the test
 | 
			
		||||
            raise
 | 
			
		||||
 | 
			
		||||
    def test_query_counter(self):
 | 
			
		||||
        connect('mongoenginetest')
 | 
			
		||||
        connect("mongoenginetest")
 | 
			
		||||
        db = get_db()
 | 
			
		||||
 | 
			
		||||
        collection = db.query_counter
 | 
			
		||||
@@ -245,7 +248,7 @@ class ContextManagersTest(unittest.TestCase):
 | 
			
		||||
            count_documents(collection, {})
 | 
			
		||||
 | 
			
		||||
        def issue_1_insert_query():
 | 
			
		||||
            collection.insert_one({'test': 'garbage'})
 | 
			
		||||
            collection.insert_one({"test": "garbage"})
 | 
			
		||||
 | 
			
		||||
        def issue_1_find_query():
 | 
			
		||||
            collection.find_one()
 | 
			
		||||
@@ -253,7 +256,9 @@ class ContextManagersTest(unittest.TestCase):
 | 
			
		||||
        counter = 0
 | 
			
		||||
        with query_counter() as q:
 | 
			
		||||
            self.assertEqual(q, counter)
 | 
			
		||||
            self.assertEqual(q, counter)    # Ensures previous count query did not get counted
 | 
			
		||||
            self.assertEqual(
 | 
			
		||||
                q, counter
 | 
			
		||||
            )  # Ensures previous count query did not get counted
 | 
			
		||||
 | 
			
		||||
            for _ in range(10):
 | 
			
		||||
                issue_1_insert_query()
 | 
			
		||||
@@ -270,23 +275,25 @@ class ContextManagersTest(unittest.TestCase):
 | 
			
		||||
                counter += 1
 | 
			
		||||
            self.assertEqual(q, counter)
 | 
			
		||||
 | 
			
		||||
            self.assertEqual(int(q), counter)       # test __int__
 | 
			
		||||
            self.assertEqual(int(q), counter)  # test __int__
 | 
			
		||||
            self.assertEqual(repr(q), str(int(q)))  # test __repr__
 | 
			
		||||
            self.assertGreater(q, -1)               # test __gt__
 | 
			
		||||
            self.assertGreaterEqual(q, int(q))      # test __gte__
 | 
			
		||||
            self.assertGreater(q, -1)  # test __gt__
 | 
			
		||||
            self.assertGreaterEqual(q, int(q))  # test __gte__
 | 
			
		||||
            self.assertNotEqual(q, -1)
 | 
			
		||||
            self.assertLess(q, 1000)
 | 
			
		||||
            self.assertLessEqual(q, int(q))
 | 
			
		||||
 | 
			
		||||
    def test_query_counter_counts_getmore_queries(self):
 | 
			
		||||
        connect('mongoenginetest')
 | 
			
		||||
        connect("mongoenginetest")
 | 
			
		||||
        db = get_db()
 | 
			
		||||
 | 
			
		||||
        collection = db.query_counter
 | 
			
		||||
        collection.drop()
 | 
			
		||||
 | 
			
		||||
        many_docs = [{'test': 'garbage %s' % i} for i in range(150)]
 | 
			
		||||
        collection.insert_many(many_docs)   # first batch of documents contains 101 documents
 | 
			
		||||
        many_docs = [{"test": "garbage %s" % i} for i in range(150)]
 | 
			
		||||
        collection.insert_many(
 | 
			
		||||
            many_docs
 | 
			
		||||
        )  # first batch of documents contains 101 documents
 | 
			
		||||
 | 
			
		||||
        with query_counter() as q:
 | 
			
		||||
            self.assertEqual(q, 0)
 | 
			
		||||
@@ -294,24 +301,26 @@ class ContextManagersTest(unittest.TestCase):
 | 
			
		||||
            self.assertEqual(q, 2)  # 1st select + 1 getmore
 | 
			
		||||
 | 
			
		||||
    def test_query_counter_ignores_particular_queries(self):
 | 
			
		||||
        connect('mongoenginetest')
 | 
			
		||||
        connect("mongoenginetest")
 | 
			
		||||
        db = get_db()
 | 
			
		||||
 | 
			
		||||
        collection = db.query_counter
 | 
			
		||||
        collection.insert_many([{'test': 'garbage %s' % i} for i in range(10)])
 | 
			
		||||
        collection.insert_many([{"test": "garbage %s" % i} for i in range(10)])
 | 
			
		||||
 | 
			
		||||
        with query_counter() as q:
 | 
			
		||||
            self.assertEqual(q, 0)
 | 
			
		||||
            cursor = collection.find()
 | 
			
		||||
            self.assertEqual(q, 0)      # cursor wasn't opened yet
 | 
			
		||||
            _ = next(cursor)            # opens the cursor and fires the find query
 | 
			
		||||
            self.assertEqual(q, 0)  # cursor wasn't opened yet
 | 
			
		||||
            _ = next(cursor)  # opens the cursor and fires the find query
 | 
			
		||||
            self.assertEqual(q, 1)
 | 
			
		||||
 | 
			
		||||
            cursor.close()              # issues a `killcursors` query that is ignored by the context
 | 
			
		||||
            cursor.close()  # issues a `killcursors` query that is ignored by the context
 | 
			
		||||
            self.assertEqual(q, 1)
 | 
			
		||||
            _ = db.system.indexes.find_one()    # queries on db.system.indexes are ignored as well
 | 
			
		||||
            _ = (
 | 
			
		||||
                db.system.indexes.find_one()
 | 
			
		||||
            )  # queries on db.system.indexes are ignored as well
 | 
			
		||||
            self.assertEqual(q, 1)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    unittest.main()
 | 
			
		||||
 
 | 
			
		||||
@@ -14,128 +14,129 @@ class DocumentStub(object):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestBaseDict(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _get_basedict(dict_items):
 | 
			
		||||
        """Get a BaseList bound to a fake document instance"""
 | 
			
		||||
        fake_doc = DocumentStub()
 | 
			
		||||
        base_list = BaseDict(dict_items, instance=None, name='my_name')
 | 
			
		||||
        base_list._instance = fake_doc  # hack to inject the mock, it does not work in the constructor
 | 
			
		||||
        base_list = BaseDict(dict_items, instance=None, name="my_name")
 | 
			
		||||
        base_list._instance = (
 | 
			
		||||
            fake_doc
 | 
			
		||||
        )  # hack to inject the mock, it does not work in the constructor
 | 
			
		||||
        return base_list
 | 
			
		||||
 | 
			
		||||
    def test___init___(self):
 | 
			
		||||
        class MyDoc(Document):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        dict_items = {'k': 'v'}
 | 
			
		||||
        dict_items = {"k": "v"}
 | 
			
		||||
        doc = MyDoc()
 | 
			
		||||
        base_dict = BaseDict(dict_items, instance=doc, name='my_name')
 | 
			
		||||
        base_dict = BaseDict(dict_items, instance=doc, name="my_name")
 | 
			
		||||
        self.assertIsInstance(base_dict._instance, Document)
 | 
			
		||||
        self.assertEqual(base_dict._name, 'my_name')
 | 
			
		||||
        self.assertEqual(base_dict._name, "my_name")
 | 
			
		||||
        self.assertEqual(base_dict, dict_items)
 | 
			
		||||
 | 
			
		||||
    def test_setdefault_calls_mark_as_changed(self):
 | 
			
		||||
        base_dict = self._get_basedict({})
 | 
			
		||||
        base_dict.setdefault('k', 'v')
 | 
			
		||||
        base_dict.setdefault("k", "v")
 | 
			
		||||
        self.assertEqual(base_dict._instance._changed_fields, [base_dict._name])
 | 
			
		||||
 | 
			
		||||
    def test_popitems_calls_mark_as_changed(self):
 | 
			
		||||
        base_dict = self._get_basedict({'k': 'v'})
 | 
			
		||||
        self.assertEqual(base_dict.popitem(), ('k', 'v'))
 | 
			
		||||
        base_dict = self._get_basedict({"k": "v"})
 | 
			
		||||
        self.assertEqual(base_dict.popitem(), ("k", "v"))
 | 
			
		||||
        self.assertEqual(base_dict._instance._changed_fields, [base_dict._name])
 | 
			
		||||
        self.assertFalse(base_dict)
 | 
			
		||||
 | 
			
		||||
    def test_pop_calls_mark_as_changed(self):
 | 
			
		||||
        base_dict = self._get_basedict({'k': 'v'})
 | 
			
		||||
        self.assertEqual(base_dict.pop('k'), 'v')
 | 
			
		||||
        base_dict = self._get_basedict({"k": "v"})
 | 
			
		||||
        self.assertEqual(base_dict.pop("k"), "v")
 | 
			
		||||
        self.assertEqual(base_dict._instance._changed_fields, [base_dict._name])
 | 
			
		||||
        self.assertFalse(base_dict)
 | 
			
		||||
 | 
			
		||||
    def test_pop_calls_does_not_mark_as_changed_when_it_fails(self):
 | 
			
		||||
        base_dict = self._get_basedict({'k': 'v'})
 | 
			
		||||
        base_dict = self._get_basedict({"k": "v"})
 | 
			
		||||
        with self.assertRaises(KeyError):
 | 
			
		||||
            base_dict.pop('X')
 | 
			
		||||
            base_dict.pop("X")
 | 
			
		||||
        self.assertFalse(base_dict._instance._changed_fields)
 | 
			
		||||
 | 
			
		||||
    def test_clear_calls_mark_as_changed(self):
 | 
			
		||||
        base_dict = self._get_basedict({'k': 'v'})
 | 
			
		||||
        base_dict = self._get_basedict({"k": "v"})
 | 
			
		||||
        base_dict.clear()
 | 
			
		||||
        self.assertEqual(base_dict._instance._changed_fields, ['my_name'])
 | 
			
		||||
        self.assertEqual(base_dict._instance._changed_fields, ["my_name"])
 | 
			
		||||
        self.assertEqual(base_dict, {})
 | 
			
		||||
 | 
			
		||||
    def test___delitem___calls_mark_as_changed(self):
 | 
			
		||||
        base_dict = self._get_basedict({'k': 'v'})
 | 
			
		||||
        del base_dict['k']
 | 
			
		||||
        self.assertEqual(base_dict._instance._changed_fields, ['my_name.k'])
 | 
			
		||||
        base_dict = self._get_basedict({"k": "v"})
 | 
			
		||||
        del base_dict["k"]
 | 
			
		||||
        self.assertEqual(base_dict._instance._changed_fields, ["my_name.k"])
 | 
			
		||||
        self.assertEqual(base_dict, {})
 | 
			
		||||
 | 
			
		||||
    def test___getitem____KeyError(self):
 | 
			
		||||
        base_dict = self._get_basedict({})
 | 
			
		||||
        with self.assertRaises(KeyError):
 | 
			
		||||
            base_dict['new']
 | 
			
		||||
            base_dict["new"]
 | 
			
		||||
 | 
			
		||||
    def test___getitem____simple_value(self):
 | 
			
		||||
        base_dict = self._get_basedict({'k': 'v'})
 | 
			
		||||
        base_dict['k'] = 'v'
 | 
			
		||||
        base_dict = self._get_basedict({"k": "v"})
 | 
			
		||||
        base_dict["k"] = "v"
 | 
			
		||||
 | 
			
		||||
    def test___getitem____sublist_gets_converted_to_BaseList(self):
 | 
			
		||||
        base_dict = self._get_basedict({'k': [0, 1, 2]})
 | 
			
		||||
        sub_list = base_dict['k']
 | 
			
		||||
        base_dict = self._get_basedict({"k": [0, 1, 2]})
 | 
			
		||||
        sub_list = base_dict["k"]
 | 
			
		||||
        self.assertEqual(sub_list, [0, 1, 2])
 | 
			
		||||
        self.assertIsInstance(sub_list, BaseList)
 | 
			
		||||
        self.assertIs(sub_list._instance, base_dict._instance)
 | 
			
		||||
        self.assertEqual(sub_list._name, 'my_name.k')
 | 
			
		||||
        self.assertEqual(sub_list._name, "my_name.k")
 | 
			
		||||
        self.assertEqual(base_dict._instance._changed_fields, [])
 | 
			
		||||
 | 
			
		||||
        # Challenge mark_as_changed from sublist
 | 
			
		||||
        sub_list[1] = None
 | 
			
		||||
        self.assertEqual(base_dict._instance._changed_fields, ['my_name.k.1'])
 | 
			
		||||
        self.assertEqual(base_dict._instance._changed_fields, ["my_name.k.1"])
 | 
			
		||||
 | 
			
		||||
    def test___getitem____subdict_gets_converted_to_BaseDict(self):
 | 
			
		||||
        base_dict = self._get_basedict({'k': {'subk': 'subv'}})
 | 
			
		||||
        sub_dict = base_dict['k']
 | 
			
		||||
        self.assertEqual(sub_dict, {'subk': 'subv'})
 | 
			
		||||
        base_dict = self._get_basedict({"k": {"subk": "subv"}})
 | 
			
		||||
        sub_dict = base_dict["k"]
 | 
			
		||||
        self.assertEqual(sub_dict, {"subk": "subv"})
 | 
			
		||||
        self.assertIsInstance(sub_dict, BaseDict)
 | 
			
		||||
        self.assertIs(sub_dict._instance, base_dict._instance)
 | 
			
		||||
        self.assertEqual(sub_dict._name, 'my_name.k')
 | 
			
		||||
        self.assertEqual(sub_dict._name, "my_name.k")
 | 
			
		||||
        self.assertEqual(base_dict._instance._changed_fields, [])
 | 
			
		||||
 | 
			
		||||
        # Challenge mark_as_changed from subdict
 | 
			
		||||
        sub_dict['subk'] = None
 | 
			
		||||
        self.assertEqual(base_dict._instance._changed_fields, ['my_name.k.subk'])
 | 
			
		||||
        sub_dict["subk"] = None
 | 
			
		||||
        self.assertEqual(base_dict._instance._changed_fields, ["my_name.k.subk"])
 | 
			
		||||
 | 
			
		||||
    def test_get_sublist_gets_converted_to_BaseList_just_like__getitem__(self):
 | 
			
		||||
        base_dict = self._get_basedict({'k': [0, 1, 2]})
 | 
			
		||||
        sub_list = base_dict.get('k')
 | 
			
		||||
        base_dict = self._get_basedict({"k": [0, 1, 2]})
 | 
			
		||||
        sub_list = base_dict.get("k")
 | 
			
		||||
        self.assertEqual(sub_list, [0, 1, 2])
 | 
			
		||||
        self.assertIsInstance(sub_list, BaseList)
 | 
			
		||||
 | 
			
		||||
    def test_get_returns_the_same_as___getitem__(self):
 | 
			
		||||
        base_dict = self._get_basedict({'k': [0, 1, 2]})
 | 
			
		||||
        get_ = base_dict.get('k')
 | 
			
		||||
        getitem_ = base_dict['k']
 | 
			
		||||
        base_dict = self._get_basedict({"k": [0, 1, 2]})
 | 
			
		||||
        get_ = base_dict.get("k")
 | 
			
		||||
        getitem_ = base_dict["k"]
 | 
			
		||||
        self.assertEqual(get_, getitem_)
 | 
			
		||||
 | 
			
		||||
    def test_get_default(self):
 | 
			
		||||
        base_dict = self._get_basedict({})
 | 
			
		||||
        sentinel = object()
 | 
			
		||||
        self.assertEqual(base_dict.get('new'), None)
 | 
			
		||||
        self.assertIs(base_dict.get('new', sentinel), sentinel)
 | 
			
		||||
        self.assertEqual(base_dict.get("new"), None)
 | 
			
		||||
        self.assertIs(base_dict.get("new", sentinel), sentinel)
 | 
			
		||||
 | 
			
		||||
    def test___setitem___calls_mark_as_changed(self):
 | 
			
		||||
        base_dict = self._get_basedict({})
 | 
			
		||||
        base_dict['k'] = 'v'
 | 
			
		||||
        self.assertEqual(base_dict._instance._changed_fields, ['my_name.k'])
 | 
			
		||||
        self.assertEqual(base_dict, {'k': 'v'})
 | 
			
		||||
        base_dict["k"] = "v"
 | 
			
		||||
        self.assertEqual(base_dict._instance._changed_fields, ["my_name.k"])
 | 
			
		||||
        self.assertEqual(base_dict, {"k": "v"})
 | 
			
		||||
 | 
			
		||||
    def test_update_calls_mark_as_changed(self):
 | 
			
		||||
        base_dict = self._get_basedict({})
 | 
			
		||||
        base_dict.update({'k': 'v'})
 | 
			
		||||
        self.assertEqual(base_dict._instance._changed_fields, ['my_name'])
 | 
			
		||||
        base_dict.update({"k": "v"})
 | 
			
		||||
        self.assertEqual(base_dict._instance._changed_fields, ["my_name"])
 | 
			
		||||
 | 
			
		||||
    def test___setattr____not_tracked_by_changes(self):
 | 
			
		||||
        base_dict = self._get_basedict({})
 | 
			
		||||
        base_dict.a_new_attr = 'test'
 | 
			
		||||
        base_dict.a_new_attr = "test"
 | 
			
		||||
        self.assertEqual(base_dict._instance._changed_fields, [])
 | 
			
		||||
 | 
			
		||||
    def test___delattr____tracked_by_changes(self):
 | 
			
		||||
@@ -143,19 +144,20 @@ class TestBaseDict(unittest.TestCase):
 | 
			
		||||
        # This is even bad because it could be that there is an attribute
 | 
			
		||||
        # with the same name as a key
 | 
			
		||||
        base_dict = self._get_basedict({})
 | 
			
		||||
        base_dict.a_new_attr = 'test'
 | 
			
		||||
        base_dict.a_new_attr = "test"
 | 
			
		||||
        del base_dict.a_new_attr
 | 
			
		||||
        self.assertEqual(base_dict._instance._changed_fields, ['my_name.a_new_attr'])
 | 
			
		||||
        self.assertEqual(base_dict._instance._changed_fields, ["my_name.a_new_attr"])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestBaseList(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _get_baselist(list_items):
 | 
			
		||||
        """Get a BaseList bound to a fake document instance"""
 | 
			
		||||
        fake_doc = DocumentStub()
 | 
			
		||||
        base_list = BaseList(list_items, instance=None, name='my_name')
 | 
			
		||||
        base_list._instance = fake_doc  # hack to inject the mock, it does not work in the constructor
 | 
			
		||||
        base_list = BaseList(list_items, instance=None, name="my_name")
 | 
			
		||||
        base_list._instance = (
 | 
			
		||||
            fake_doc
 | 
			
		||||
        )  # hack to inject the mock, it does not work in the constructor
 | 
			
		||||
        return base_list
 | 
			
		||||
 | 
			
		||||
    def test___init___(self):
 | 
			
		||||
@@ -164,19 +166,19 @@ class TestBaseList(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
        list_items = [True]
 | 
			
		||||
        doc = MyDoc()
 | 
			
		||||
        base_list = BaseList(list_items, instance=doc, name='my_name')
 | 
			
		||||
        base_list = BaseList(list_items, instance=doc, name="my_name")
 | 
			
		||||
        self.assertIsInstance(base_list._instance, Document)
 | 
			
		||||
        self.assertEqual(base_list._name, 'my_name')
 | 
			
		||||
        self.assertEqual(base_list._name, "my_name")
 | 
			
		||||
        self.assertEqual(base_list, list_items)
 | 
			
		||||
 | 
			
		||||
    def test___iter__(self):
 | 
			
		||||
        values = [True, False, True, False]
 | 
			
		||||
        base_list = BaseList(values, instance=None, name='my_name')
 | 
			
		||||
        base_list = BaseList(values, instance=None, name="my_name")
 | 
			
		||||
        self.assertEqual(values, list(base_list))
 | 
			
		||||
 | 
			
		||||
    def test___iter___allow_modification_while_iterating_withou_error(self):
 | 
			
		||||
        # regular list allows for this, thus this subclass must comply to that
 | 
			
		||||
        base_list = BaseList([True, False, True, False], instance=None, name='my_name')
 | 
			
		||||
        base_list = BaseList([True, False, True, False], instance=None, name="my_name")
 | 
			
		||||
        for idx, val in enumerate(base_list):
 | 
			
		||||
            if val:
 | 
			
		||||
                base_list.pop(idx)
 | 
			
		||||
@@ -185,7 +187,7 @@ class TestBaseList(unittest.TestCase):
 | 
			
		||||
        base_list = self._get_baselist([])
 | 
			
		||||
        self.assertFalse(base_list._instance._changed_fields)
 | 
			
		||||
        base_list.append(True)
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ['my_name'])
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ["my_name"])
 | 
			
		||||
 | 
			
		||||
    def test_subclass_append(self):
 | 
			
		||||
        # Due to the way mark_as_changed_wrapper is implemented
 | 
			
		||||
@@ -193,7 +195,7 @@ class TestBaseList(unittest.TestCase):
 | 
			
		||||
        class SubBaseList(BaseList):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        base_list = SubBaseList([], instance=None, name='my_name')
 | 
			
		||||
        base_list = SubBaseList([], instance=None, name="my_name")
 | 
			
		||||
        base_list.append(True)
 | 
			
		||||
 | 
			
		||||
    def test___getitem__using_simple_index(self):
 | 
			
		||||
@@ -217,54 +219,45 @@ class TestBaseList(unittest.TestCase):
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, [])
 | 
			
		||||
 | 
			
		||||
    def test___getitem__sublist_returns_BaseList_bound_to_instance(self):
 | 
			
		||||
        base_list = self._get_baselist(
 | 
			
		||||
            [
 | 
			
		||||
                [1, 2],
 | 
			
		||||
                [3, 4]
 | 
			
		||||
            ]
 | 
			
		||||
        )
 | 
			
		||||
        base_list = self._get_baselist([[1, 2], [3, 4]])
 | 
			
		||||
        sub_list = base_list[0]
 | 
			
		||||
        self.assertEqual(sub_list, [1, 2])
 | 
			
		||||
        self.assertIsInstance(sub_list, BaseList)
 | 
			
		||||
        self.assertIs(sub_list._instance, base_list._instance)
 | 
			
		||||
        self.assertEqual(sub_list._name, 'my_name.0')
 | 
			
		||||
        self.assertEqual(sub_list._name, "my_name.0")
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, [])
 | 
			
		||||
 | 
			
		||||
        # Challenge mark_as_changed from sublist
 | 
			
		||||
        sub_list[1] = None
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ['my_name.0.1'])
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ["my_name.0.1"])
 | 
			
		||||
 | 
			
		||||
    def test___getitem__subdict_returns_BaseList_bound_to_instance(self):
 | 
			
		||||
        base_list = self._get_baselist(
 | 
			
		||||
            [
 | 
			
		||||
                {'subk': 'subv'}
 | 
			
		||||
            ]
 | 
			
		||||
        )
 | 
			
		||||
        base_list = self._get_baselist([{"subk": "subv"}])
 | 
			
		||||
        sub_dict = base_list[0]
 | 
			
		||||
        self.assertEqual(sub_dict, {'subk': 'subv'})
 | 
			
		||||
        self.assertEqual(sub_dict, {"subk": "subv"})
 | 
			
		||||
        self.assertIsInstance(sub_dict, BaseDict)
 | 
			
		||||
        self.assertIs(sub_dict._instance, base_list._instance)
 | 
			
		||||
        self.assertEqual(sub_dict._name, 'my_name.0')
 | 
			
		||||
        self.assertEqual(sub_dict._name, "my_name.0")
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, [])
 | 
			
		||||
 | 
			
		||||
        # Challenge mark_as_changed from subdict
 | 
			
		||||
        sub_dict['subk'] = None
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ['my_name.0.subk'])
 | 
			
		||||
        sub_dict["subk"] = None
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ["my_name.0.subk"])
 | 
			
		||||
 | 
			
		||||
    def test_extend_calls_mark_as_changed(self):
 | 
			
		||||
        base_list = self._get_baselist([])
 | 
			
		||||
        base_list.extend([True])
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ['my_name'])
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ["my_name"])
 | 
			
		||||
 | 
			
		||||
    def test_insert_calls_mark_as_changed(self):
 | 
			
		||||
        base_list = self._get_baselist([])
 | 
			
		||||
        base_list.insert(0, True)
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ['my_name'])
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ["my_name"])
 | 
			
		||||
 | 
			
		||||
    def test_remove_calls_mark_as_changed(self):
 | 
			
		||||
        base_list = self._get_baselist([True])
 | 
			
		||||
        base_list.remove(True)
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ['my_name'])
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ["my_name"])
 | 
			
		||||
 | 
			
		||||
    def test_remove_not_mark_as_changed_when_it_fails(self):
 | 
			
		||||
        base_list = self._get_baselist([True])
 | 
			
		||||
@@ -275,70 +268,76 @@ class TestBaseList(unittest.TestCase):
 | 
			
		||||
    def test_pop_calls_mark_as_changed(self):
 | 
			
		||||
        base_list = self._get_baselist([True])
 | 
			
		||||
        base_list.pop()
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ['my_name'])
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ["my_name"])
 | 
			
		||||
 | 
			
		||||
    def test_reverse_calls_mark_as_changed(self):
 | 
			
		||||
        base_list = self._get_baselist([True, False])
 | 
			
		||||
        base_list.reverse()
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ['my_name'])
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ["my_name"])
 | 
			
		||||
 | 
			
		||||
    def test___delitem___calls_mark_as_changed(self):
 | 
			
		||||
        base_list = self._get_baselist([True])
 | 
			
		||||
        del base_list[0]
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ['my_name'])
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ["my_name"])
 | 
			
		||||
 | 
			
		||||
    def test___setitem___calls_with_full_slice_mark_as_changed(self):
 | 
			
		||||
        base_list = self._get_baselist([])
 | 
			
		||||
        base_list[:] = [0, 1]      # Will use __setslice__ under py2 and __setitem__ under py3
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ['my_name'])
 | 
			
		||||
        base_list[:] = [
 | 
			
		||||
            0,
 | 
			
		||||
            1,
 | 
			
		||||
        ]  # Will use __setslice__ under py2 and __setitem__ under py3
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ["my_name"])
 | 
			
		||||
        self.assertEqual(base_list, [0, 1])
 | 
			
		||||
 | 
			
		||||
    def test___setitem___calls_with_partial_slice_mark_as_changed(self):
 | 
			
		||||
        base_list = self._get_baselist([0, 1, 2])
 | 
			
		||||
        base_list[0:2] = [1, 0]     # Will use __setslice__ under py2 and __setitem__ under py3
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ['my_name'])
 | 
			
		||||
        base_list[0:2] = [
 | 
			
		||||
            1,
 | 
			
		||||
            0,
 | 
			
		||||
        ]  # Will use __setslice__ under py2 and __setitem__ under py3
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ["my_name"])
 | 
			
		||||
        self.assertEqual(base_list, [1, 0, 2])
 | 
			
		||||
 | 
			
		||||
    def test___setitem___calls_with_step_slice_mark_as_changed(self):
 | 
			
		||||
        base_list = self._get_baselist([0, 1, 2])
 | 
			
		||||
        base_list[0:3:2] = [-1, -2]   # uses __setitem__ in both py2 & 3
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ['my_name'])
 | 
			
		||||
        base_list[0:3:2] = [-1, -2]  # uses __setitem__ in both py2 & 3
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ["my_name"])
 | 
			
		||||
        self.assertEqual(base_list, [-1, 1, -2])
 | 
			
		||||
 | 
			
		||||
    def test___setitem___with_slice(self):
 | 
			
		||||
        base_list = self._get_baselist([0, 1, 2, 3, 4, 5])
 | 
			
		||||
        base_list[0:6:2] = [None, None, None]
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ['my_name'])
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ["my_name"])
 | 
			
		||||
        self.assertEqual(base_list, [None, 1, None, 3, None, 5])
 | 
			
		||||
 | 
			
		||||
    def test___setitem___item_0_calls_mark_as_changed(self):
 | 
			
		||||
        base_list = self._get_baselist([True])
 | 
			
		||||
        base_list[0] = False
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ['my_name'])
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ["my_name"])
 | 
			
		||||
        self.assertEqual(base_list, [False])
 | 
			
		||||
 | 
			
		||||
    def test___setitem___item_1_calls_mark_as_changed(self):
 | 
			
		||||
        base_list = self._get_baselist([True, True])
 | 
			
		||||
        base_list[1] = False
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ['my_name.1'])
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ["my_name.1"])
 | 
			
		||||
        self.assertEqual(base_list, [True, False])
 | 
			
		||||
 | 
			
		||||
    def test___delslice___calls_mark_as_changed(self):
 | 
			
		||||
        base_list = self._get_baselist([0, 1])
 | 
			
		||||
        del base_list[0:1]
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ['my_name'])
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ["my_name"])
 | 
			
		||||
        self.assertEqual(base_list, [1])
 | 
			
		||||
 | 
			
		||||
    def test___iadd___calls_mark_as_changed(self):
 | 
			
		||||
        base_list = self._get_baselist([True])
 | 
			
		||||
        base_list += [False]
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ['my_name'])
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ["my_name"])
 | 
			
		||||
 | 
			
		||||
    def test___imul___calls_mark_as_changed(self):
 | 
			
		||||
        base_list = self._get_baselist([True])
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, [])
 | 
			
		||||
        base_list *= 2
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ['my_name'])
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ["my_name"])
 | 
			
		||||
 | 
			
		||||
    def test_sort_calls_not_marked_as_changed_when_it_fails(self):
 | 
			
		||||
        base_list = self._get_baselist([True])
 | 
			
		||||
@@ -350,7 +349,7 @@ class TestBaseList(unittest.TestCase):
 | 
			
		||||
    def test_sort_calls_mark_as_changed(self):
 | 
			
		||||
        base_list = self._get_baselist([True, False])
 | 
			
		||||
        base_list.sort()
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ['my_name'])
 | 
			
		||||
        self.assertEqual(base_list._instance._changed_fields, ["my_name"])
 | 
			
		||||
 | 
			
		||||
    def test_sort_calls_with_key(self):
 | 
			
		||||
        base_list = self._get_baselist([1, 2, 11])
 | 
			
		||||
@@ -371,7 +370,7 @@ class TestStrictDict(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    def test_iterkeys(self):
 | 
			
		||||
        d = self.dtype(a=1)
 | 
			
		||||
        self.assertEqual(list(iterkeys(d)), ['a'])
 | 
			
		||||
        self.assertEqual(list(iterkeys(d)), ["a"])
 | 
			
		||||
 | 
			
		||||
    def test_len(self):
 | 
			
		||||
        d = self.dtype(a=1)
 | 
			
		||||
@@ -379,9 +378,9 @@ class TestStrictDict(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    def test_pop(self):
 | 
			
		||||
        d = self.dtype(a=1)
 | 
			
		||||
        self.assertIn('a', d)
 | 
			
		||||
        d.pop('a')
 | 
			
		||||
        self.assertNotIn('a', d)
 | 
			
		||||
        self.assertIn("a", d)
 | 
			
		||||
        d.pop("a")
 | 
			
		||||
        self.assertNotIn("a", d)
 | 
			
		||||
 | 
			
		||||
    def test_repr(self):
 | 
			
		||||
        d = self.dtype(a=1, b=2, c=3)
 | 
			
		||||
@@ -416,7 +415,7 @@ class TestStrictDict(unittest.TestCase):
 | 
			
		||||
        d = self.dtype()
 | 
			
		||||
        d.a = 1
 | 
			
		||||
        self.assertEqual(d.a, 1)
 | 
			
		||||
        self.assertRaises(AttributeError, getattr, d, 'b')
 | 
			
		||||
        self.assertRaises(AttributeError, getattr, d, "b")
 | 
			
		||||
 | 
			
		||||
    def test_setattr_raises_on_nonexisting_attr(self):
 | 
			
		||||
        d = self.dtype()
 | 
			
		||||
@@ -430,20 +429,20 @@ class TestStrictDict(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    def test_get(self):
 | 
			
		||||
        d = self.dtype(a=1)
 | 
			
		||||
        self.assertEqual(d.get('a'), 1)
 | 
			
		||||
        self.assertEqual(d.get('b', 'bla'), 'bla')
 | 
			
		||||
        self.assertEqual(d.get("a"), 1)
 | 
			
		||||
        self.assertEqual(d.get("b", "bla"), "bla")
 | 
			
		||||
 | 
			
		||||
    def test_items(self):
 | 
			
		||||
        d = self.dtype(a=1)
 | 
			
		||||
        self.assertEqual(d.items(), [('a', 1)])
 | 
			
		||||
        self.assertEqual(d.items(), [("a", 1)])
 | 
			
		||||
        d = self.dtype(a=1, b=2)
 | 
			
		||||
        self.assertEqual(d.items(), [('a', 1), ('b', 2)])
 | 
			
		||||
        self.assertEqual(d.items(), [("a", 1), ("b", 2)])
 | 
			
		||||
 | 
			
		||||
    def test_mappings_protocol(self):
 | 
			
		||||
        d = self.dtype(a=1, b=2)
 | 
			
		||||
        self.assertEqual(dict(d), {'a': 1, 'b': 2})
 | 
			
		||||
        self.assertEqual(dict(**d), {'a': 1, 'b': 2})
 | 
			
		||||
        self.assertEqual(dict(d), {"a": 1, "b": 2})
 | 
			
		||||
        self.assertEqual(dict(**d), {"a": 1, "b": 2})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    unittest.main()
 | 
			
		||||
 
 | 
			
		||||
@@ -10,18 +10,18 @@ from mongoengine.context_managers import query_counter
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FieldTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def setUpClass(cls):
 | 
			
		||||
        cls.db = connect(db='mongoenginetest')
 | 
			
		||||
        cls.db = connect(db="mongoenginetest")
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def tearDownClass(cls):
 | 
			
		||||
        cls.db.drop_database('mongoenginetest')
 | 
			
		||||
        cls.db.drop_database("mongoenginetest")
 | 
			
		||||
 | 
			
		||||
    def test_list_item_dereference(self):
 | 
			
		||||
        """Ensure that DBRef items in ListFields are dereferenced.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class User(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
@@ -32,7 +32,7 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
        Group.drop_collection()
 | 
			
		||||
 | 
			
		||||
        for i in range(1, 51):
 | 
			
		||||
            user = User(name='user %s' % i)
 | 
			
		||||
            user = User(name="user %s" % i)
 | 
			
		||||
            user.save()
 | 
			
		||||
 | 
			
		||||
        group = Group(members=User.objects)
 | 
			
		||||
@@ -47,7 +47,7 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
            group_obj = Group.objects.first()
 | 
			
		||||
            self.assertEqual(q, 1)
 | 
			
		||||
 | 
			
		||||
            len(group_obj._data['members'])
 | 
			
		||||
            len(group_obj._data["members"])
 | 
			
		||||
            self.assertEqual(q, 1)
 | 
			
		||||
 | 
			
		||||
            len(group_obj.members)
 | 
			
		||||
@@ -80,6 +80,7 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
    def test_list_item_dereference_dref_false(self):
 | 
			
		||||
        """Ensure that DBRef items in ListFields are dereferenced.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class User(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
@@ -90,7 +91,7 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
        Group.drop_collection()
 | 
			
		||||
 | 
			
		||||
        for i in range(1, 51):
 | 
			
		||||
            user = User(name='user %s' % i)
 | 
			
		||||
            user = User(name="user %s" % i)
 | 
			
		||||
            user.save()
 | 
			
		||||
 | 
			
		||||
        group = Group(members=User.objects)
 | 
			
		||||
@@ -105,14 +106,14 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
            [m for m in group_obj.members]
 | 
			
		||||
            self.assertEqual(q, 2)
 | 
			
		||||
            self.assertTrue(group_obj._data['members']._dereferenced)
 | 
			
		||||
            self.assertTrue(group_obj._data["members"]._dereferenced)
 | 
			
		||||
 | 
			
		||||
            # verifies that no additional queries gets executed
 | 
			
		||||
            # if we re-iterate over the ListField once it is
 | 
			
		||||
            # dereferenced
 | 
			
		||||
            [m for m in group_obj.members]
 | 
			
		||||
            self.assertEqual(q, 2)
 | 
			
		||||
            self.assertTrue(group_obj._data['members']._dereferenced)
 | 
			
		||||
            self.assertTrue(group_obj._data["members"]._dereferenced)
 | 
			
		||||
 | 
			
		||||
        # Document select_related
 | 
			
		||||
        with query_counter() as q:
 | 
			
		||||
@@ -136,6 +137,7 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
    def test_list_item_dereference_orphan_dbref(self):
 | 
			
		||||
        """Ensure that orphan DBRef items in ListFields are dereferenced.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class User(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
@@ -146,7 +148,7 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
        Group.drop_collection()
 | 
			
		||||
 | 
			
		||||
        for i in range(1, 51):
 | 
			
		||||
            user = User(name='user %s' % i)
 | 
			
		||||
            user = User(name="user %s" % i)
 | 
			
		||||
            user.save()
 | 
			
		||||
 | 
			
		||||
        group = Group(members=User.objects)
 | 
			
		||||
@@ -164,14 +166,14 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
            [m for m in group_obj.members]
 | 
			
		||||
            self.assertEqual(q, 2)
 | 
			
		||||
            self.assertTrue(group_obj._data['members']._dereferenced)
 | 
			
		||||
            self.assertTrue(group_obj._data["members"]._dereferenced)
 | 
			
		||||
 | 
			
		||||
            # verifies that no additional queries gets executed
 | 
			
		||||
            # if we re-iterate over the ListField once it is
 | 
			
		||||
            # dereferenced
 | 
			
		||||
            [m for m in group_obj.members]
 | 
			
		||||
            self.assertEqual(q, 2)
 | 
			
		||||
            self.assertTrue(group_obj._data['members']._dereferenced)
 | 
			
		||||
            self.assertTrue(group_obj._data["members"]._dereferenced)
 | 
			
		||||
 | 
			
		||||
        User.drop_collection()
 | 
			
		||||
        Group.drop_collection()
 | 
			
		||||
@@ -179,6 +181,7 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
    def test_list_item_dereference_dref_false_stores_as_type(self):
 | 
			
		||||
        """Ensure that DBRef items are stored as their type
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class User(Document):
 | 
			
		||||
            my_id = IntField(primary_key=True)
 | 
			
		||||
            name = StringField()
 | 
			
		||||
@@ -189,17 +192,18 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
        User.drop_collection()
 | 
			
		||||
        Group.drop_collection()
 | 
			
		||||
 | 
			
		||||
        user = User(my_id=1, name='user 1').save()
 | 
			
		||||
        user = User(my_id=1, name="user 1").save()
 | 
			
		||||
 | 
			
		||||
        Group(members=User.objects).save()
 | 
			
		||||
        group = Group.objects.first()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(Group._get_collection().find_one()['members'], [1])
 | 
			
		||||
        self.assertEqual(Group._get_collection().find_one()["members"], [1])
 | 
			
		||||
        self.assertEqual(group.members, [user])
 | 
			
		||||
 | 
			
		||||
    def test_handle_old_style_references(self):
 | 
			
		||||
        """Ensure that DBRef items in ListFields are dereferenced.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class User(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
@@ -210,7 +214,7 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
        Group.drop_collection()
 | 
			
		||||
 | 
			
		||||
        for i in range(1, 26):
 | 
			
		||||
            user = User(name='user %s' % i)
 | 
			
		||||
            user = User(name="user %s" % i)
 | 
			
		||||
            user.save()
 | 
			
		||||
 | 
			
		||||
        group = Group(members=User.objects)
 | 
			
		||||
@@ -227,8 +231,8 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
        group.save()
 | 
			
		||||
 | 
			
		||||
        group = Group.objects.first()
 | 
			
		||||
        self.assertEqual(group.members[0].name, 'user 1')
 | 
			
		||||
        self.assertEqual(group.members[-1].name, 'String!')
 | 
			
		||||
        self.assertEqual(group.members[0].name, "user 1")
 | 
			
		||||
        self.assertEqual(group.members[-1].name, "String!")
 | 
			
		||||
 | 
			
		||||
    def test_migrate_references(self):
 | 
			
		||||
        """Example of migrating ReferenceField storage
 | 
			
		||||
@@ -249,8 +253,8 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
        group = Group(author=user, members=[user]).save()
 | 
			
		||||
 | 
			
		||||
        raw_data = Group._get_collection().find_one()
 | 
			
		||||
        self.assertIsInstance(raw_data['author'], DBRef)
 | 
			
		||||
        self.assertIsInstance(raw_data['members'][0], DBRef)
 | 
			
		||||
        self.assertIsInstance(raw_data["author"], DBRef)
 | 
			
		||||
        self.assertIsInstance(raw_data["members"][0], DBRef)
 | 
			
		||||
        group = Group.objects.first()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(group.author, user)
 | 
			
		||||
@@ -264,8 +268,8 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
        # Migrate the data
 | 
			
		||||
        for g in Group.objects():
 | 
			
		||||
            # Explicitly mark as changed so resets
 | 
			
		||||
            g._mark_as_changed('author')
 | 
			
		||||
            g._mark_as_changed('members')
 | 
			
		||||
            g._mark_as_changed("author")
 | 
			
		||||
            g._mark_as_changed("members")
 | 
			
		||||
            g.save()
 | 
			
		||||
 | 
			
		||||
        group = Group.objects.first()
 | 
			
		||||
@@ -273,35 +277,36 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
        self.assertEqual(group.members, [user])
 | 
			
		||||
 | 
			
		||||
        raw_data = Group._get_collection().find_one()
 | 
			
		||||
        self.assertIsInstance(raw_data['author'], ObjectId)
 | 
			
		||||
        self.assertIsInstance(raw_data['members'][0], ObjectId)
 | 
			
		||||
        self.assertIsInstance(raw_data["author"], ObjectId)
 | 
			
		||||
        self.assertIsInstance(raw_data["members"][0], ObjectId)
 | 
			
		||||
 | 
			
		||||
    def test_recursive_reference(self):
 | 
			
		||||
        """Ensure that ReferenceFields can reference their own documents.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Employee(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            boss = ReferenceField('self')
 | 
			
		||||
            friends = ListField(ReferenceField('self'))
 | 
			
		||||
            boss = ReferenceField("self")
 | 
			
		||||
            friends = ListField(ReferenceField("self"))
 | 
			
		||||
 | 
			
		||||
        Employee.drop_collection()
 | 
			
		||||
 | 
			
		||||
        bill = Employee(name='Bill Lumbergh')
 | 
			
		||||
        bill = Employee(name="Bill Lumbergh")
 | 
			
		||||
        bill.save()
 | 
			
		||||
 | 
			
		||||
        michael = Employee(name='Michael Bolton')
 | 
			
		||||
        michael = Employee(name="Michael Bolton")
 | 
			
		||||
        michael.save()
 | 
			
		||||
 | 
			
		||||
        samir = Employee(name='Samir Nagheenanajar')
 | 
			
		||||
        samir = Employee(name="Samir Nagheenanajar")
 | 
			
		||||
        samir.save()
 | 
			
		||||
 | 
			
		||||
        friends = [michael, samir]
 | 
			
		||||
        peter = Employee(name='Peter Gibbons', boss=bill, friends=friends)
 | 
			
		||||
        peter = Employee(name="Peter Gibbons", boss=bill, friends=friends)
 | 
			
		||||
        peter.save()
 | 
			
		||||
 | 
			
		||||
        Employee(name='Funky Gibbon', boss=bill, friends=friends).save()
 | 
			
		||||
        Employee(name='Funky Gibbon', boss=bill, friends=friends).save()
 | 
			
		||||
        Employee(name='Funky Gibbon', boss=bill, friends=friends).save()
 | 
			
		||||
        Employee(name="Funky Gibbon", boss=bill, friends=friends).save()
 | 
			
		||||
        Employee(name="Funky Gibbon", boss=bill, friends=friends).save()
 | 
			
		||||
        Employee(name="Funky Gibbon", boss=bill, friends=friends).save()
 | 
			
		||||
 | 
			
		||||
        with query_counter() as q:
 | 
			
		||||
            self.assertEqual(q, 0)
 | 
			
		||||
@@ -343,7 +348,6 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
                self.assertEqual(q, 2)
 | 
			
		||||
 | 
			
		||||
    def test_list_of_lists_of_references(self):
 | 
			
		||||
 | 
			
		||||
        class User(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
@@ -357,9 +361,9 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
        Post.drop_collection()
 | 
			
		||||
        SimpleList.drop_collection()
 | 
			
		||||
 | 
			
		||||
        u1 = User.objects.create(name='u1')
 | 
			
		||||
        u2 = User.objects.create(name='u2')
 | 
			
		||||
        u3 = User.objects.create(name='u3')
 | 
			
		||||
        u1 = User.objects.create(name="u1")
 | 
			
		||||
        u2 = User.objects.create(name="u2")
 | 
			
		||||
        u3 = User.objects.create(name="u3")
 | 
			
		||||
 | 
			
		||||
        SimpleList.objects.create(users=[u1, u2, u3])
 | 
			
		||||
        self.assertEqual(SimpleList.objects.all()[0].users, [u1, u2, u3])
 | 
			
		||||
@@ -370,13 +374,14 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
    def test_circular_reference(self):
 | 
			
		||||
        """Ensure you can handle circular references
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Relation(EmbeddedDocument):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            person = ReferenceField('Person')
 | 
			
		||||
            person = ReferenceField("Person")
 | 
			
		||||
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            relations = ListField(EmbeddedDocumentField('Relation'))
 | 
			
		||||
            relations = ListField(EmbeddedDocumentField("Relation"))
 | 
			
		||||
 | 
			
		||||
            def __repr__(self):
 | 
			
		||||
                return "<Person: %s>" % self.name
 | 
			
		||||
@@ -398,14 +403,17 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
        daughter.relations.append(self_rel)
 | 
			
		||||
        daughter.save()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual("[<Person: Mother>, <Person: Daughter>]", "%s" % Person.objects())
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            "[<Person: Mother>, <Person: Daughter>]", "%s" % Person.objects()
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_circular_reference_on_self(self):
 | 
			
		||||
        """Ensure you can handle circular references
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            relations = ListField(ReferenceField('self'))
 | 
			
		||||
            relations = ListField(ReferenceField("self"))
 | 
			
		||||
 | 
			
		||||
            def __repr__(self):
 | 
			
		||||
                return "<Person: %s>" % self.name
 | 
			
		||||
@@ -424,14 +432,17 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
        daughter.relations.append(daughter)
 | 
			
		||||
        daughter.save()
 | 
			
		||||
 | 
			
		||||
        self.assertEqual("[<Person: Mother>, <Person: Daughter>]", "%s" % Person.objects())
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            "[<Person: Mother>, <Person: Daughter>]", "%s" % Person.objects()
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_circular_tree_reference(self):
 | 
			
		||||
        """Ensure you can handle circular references with more than one level
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Other(EmbeddedDocument):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            friends = ListField(ReferenceField('Person'))
 | 
			
		||||
            friends = ListField(ReferenceField("Person"))
 | 
			
		||||
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
@@ -443,8 +454,8 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
        Person.drop_collection()
 | 
			
		||||
        paul = Person(name="Paul").save()
 | 
			
		||||
        maria = Person(name="Maria").save()
 | 
			
		||||
        julia = Person(name='Julia').save()
 | 
			
		||||
        anna = Person(name='Anna').save()
 | 
			
		||||
        julia = Person(name="Julia").save()
 | 
			
		||||
        anna = Person(name="Anna").save()
 | 
			
		||||
 | 
			
		||||
        paul.other.friends = [maria, julia, anna]
 | 
			
		||||
        paul.other.name = "Paul's friends"
 | 
			
		||||
@@ -464,11 +475,10 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            "[<Person: Paul>, <Person: Maria>, <Person: Julia>, <Person: Anna>]",
 | 
			
		||||
            "%s" % Person.objects()
 | 
			
		||||
            "%s" % Person.objects(),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_generic_reference(self):
 | 
			
		||||
 | 
			
		||||
        class UserA(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
@@ -488,13 +498,13 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
        members = []
 | 
			
		||||
        for i in range(1, 51):
 | 
			
		||||
            a = UserA(name='User A %s' % i)
 | 
			
		||||
            a = UserA(name="User A %s" % i)
 | 
			
		||||
            a.save()
 | 
			
		||||
 | 
			
		||||
            b = UserB(name='User B %s' % i)
 | 
			
		||||
            b = UserB(name="User B %s" % i)
 | 
			
		||||
            b.save()
 | 
			
		||||
 | 
			
		||||
            c = UserC(name='User C %s' % i)
 | 
			
		||||
            c = UserC(name="User C %s" % i)
 | 
			
		||||
            c.save()
 | 
			
		||||
 | 
			
		||||
            members += [a, b, c]
 | 
			
		||||
@@ -518,7 +528,7 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
            self.assertEqual(q, 4)
 | 
			
		||||
 | 
			
		||||
            for m in group_obj.members:
 | 
			
		||||
                self.assertIn('User', m.__class__.__name__)
 | 
			
		||||
                self.assertIn("User", m.__class__.__name__)
 | 
			
		||||
 | 
			
		||||
        # Document select_related
 | 
			
		||||
        with query_counter() as q:
 | 
			
		||||
@@ -534,7 +544,7 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
            self.assertEqual(q, 4)
 | 
			
		||||
 | 
			
		||||
            for m in group_obj.members:
 | 
			
		||||
                self.assertIn('User', m.__class__.__name__)
 | 
			
		||||
                self.assertIn("User", m.__class__.__name__)
 | 
			
		||||
 | 
			
		||||
        # Queryset select_related
 | 
			
		||||
        with query_counter() as q:
 | 
			
		||||
@@ -551,8 +561,7 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
                self.assertEqual(q, 4)
 | 
			
		||||
 | 
			
		||||
                for m in group_obj.members:
 | 
			
		||||
                    self.assertIn('User', m.__class__.__name__)
 | 
			
		||||
 | 
			
		||||
                    self.assertIn("User", m.__class__.__name__)
 | 
			
		||||
 | 
			
		||||
    def test_generic_reference_orphan_dbref(self):
 | 
			
		||||
        """Ensure that generic orphan DBRef items in ListFields are dereferenced.
 | 
			
		||||
@@ -577,13 +586,13 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
        members = []
 | 
			
		||||
        for i in range(1, 51):
 | 
			
		||||
            a = UserA(name='User A %s' % i)
 | 
			
		||||
            a = UserA(name="User A %s" % i)
 | 
			
		||||
            a.save()
 | 
			
		||||
 | 
			
		||||
            b = UserB(name='User B %s' % i)
 | 
			
		||||
            b = UserB(name="User B %s" % i)
 | 
			
		||||
            b.save()
 | 
			
		||||
 | 
			
		||||
            c = UserC(name='User C %s' % i)
 | 
			
		||||
            c = UserC(name="User C %s" % i)
 | 
			
		||||
            c.save()
 | 
			
		||||
 | 
			
		||||
            members += [a, b, c]
 | 
			
		||||
@@ -602,11 +611,11 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
            [m for m in group_obj.members]
 | 
			
		||||
            self.assertEqual(q, 4)
 | 
			
		||||
            self.assertTrue(group_obj._data['members']._dereferenced)
 | 
			
		||||
            self.assertTrue(group_obj._data["members"]._dereferenced)
 | 
			
		||||
 | 
			
		||||
            [m for m in group_obj.members]
 | 
			
		||||
            self.assertEqual(q, 4)
 | 
			
		||||
            self.assertTrue(group_obj._data['members']._dereferenced)
 | 
			
		||||
            self.assertTrue(group_obj._data["members"]._dereferenced)
 | 
			
		||||
 | 
			
		||||
        UserA.drop_collection()
 | 
			
		||||
        UserB.drop_collection()
 | 
			
		||||
@@ -614,7 +623,6 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
        Group.drop_collection()
 | 
			
		||||
 | 
			
		||||
    def test_list_field_complex(self):
 | 
			
		||||
 | 
			
		||||
        class UserA(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
@@ -634,13 +642,13 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
        members = []
 | 
			
		||||
        for i in range(1, 51):
 | 
			
		||||
            a = UserA(name='User A %s' % i)
 | 
			
		||||
            a = UserA(name="User A %s" % i)
 | 
			
		||||
            a.save()
 | 
			
		||||
 | 
			
		||||
            b = UserB(name='User B %s' % i)
 | 
			
		||||
            b = UserB(name="User B %s" % i)
 | 
			
		||||
            b.save()
 | 
			
		||||
 | 
			
		||||
            c = UserC(name='User C %s' % i)
 | 
			
		||||
            c = UserC(name="User C %s" % i)
 | 
			
		||||
            c.save()
 | 
			
		||||
 | 
			
		||||
            members += [a, b, c]
 | 
			
		||||
@@ -664,7 +672,7 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
            self.assertEqual(q, 4)
 | 
			
		||||
 | 
			
		||||
            for m in group_obj.members:
 | 
			
		||||
                self.assertIn('User', m.__class__.__name__)
 | 
			
		||||
                self.assertIn("User", m.__class__.__name__)
 | 
			
		||||
 | 
			
		||||
        # Document select_related
 | 
			
		||||
        with query_counter() as q:
 | 
			
		||||
@@ -680,7 +688,7 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
            self.assertEqual(q, 4)
 | 
			
		||||
 | 
			
		||||
            for m in group_obj.members:
 | 
			
		||||
                self.assertIn('User', m.__class__.__name__)
 | 
			
		||||
                self.assertIn("User", m.__class__.__name__)
 | 
			
		||||
 | 
			
		||||
        # Queryset select_related
 | 
			
		||||
        with query_counter() as q:
 | 
			
		||||
@@ -697,7 +705,7 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
                self.assertEqual(q, 4)
 | 
			
		||||
 | 
			
		||||
                for m in group_obj.members:
 | 
			
		||||
                    self.assertIn('User', m.__class__.__name__)
 | 
			
		||||
                    self.assertIn("User", m.__class__.__name__)
 | 
			
		||||
 | 
			
		||||
        UserA.drop_collection()
 | 
			
		||||
        UserB.drop_collection()
 | 
			
		||||
@@ -705,7 +713,6 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
        Group.drop_collection()
 | 
			
		||||
 | 
			
		||||
    def test_map_field_reference(self):
 | 
			
		||||
 | 
			
		||||
        class User(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
@@ -717,7 +724,7 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
        members = []
 | 
			
		||||
        for i in range(1, 51):
 | 
			
		||||
            user = User(name='user %s' % i)
 | 
			
		||||
            user = User(name="user %s" % i)
 | 
			
		||||
            user.save()
 | 
			
		||||
            members.append(user)
 | 
			
		||||
 | 
			
		||||
@@ -752,7 +759,7 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
            for k, m in iteritems(group_obj.members):
 | 
			
		||||
                self.assertIsInstance(m, User)
 | 
			
		||||
 | 
			
		||||
       # Queryset select_related
 | 
			
		||||
        # Queryset select_related
 | 
			
		||||
        with query_counter() as q:
 | 
			
		||||
            self.assertEqual(q, 0)
 | 
			
		||||
 | 
			
		||||
@@ -770,7 +777,6 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
        Group.drop_collection()
 | 
			
		||||
 | 
			
		||||
    def test_dict_field(self):
 | 
			
		||||
 | 
			
		||||
        class UserA(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
@@ -790,13 +796,13 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
        members = []
 | 
			
		||||
        for i in range(1, 51):
 | 
			
		||||
            a = UserA(name='User A %s' % i)
 | 
			
		||||
            a = UserA(name="User A %s" % i)
 | 
			
		||||
            a.save()
 | 
			
		||||
 | 
			
		||||
            b = UserB(name='User B %s' % i)
 | 
			
		||||
            b = UserB(name="User B %s" % i)
 | 
			
		||||
            b.save()
 | 
			
		||||
 | 
			
		||||
            c = UserC(name='User C %s' % i)
 | 
			
		||||
            c = UserC(name="User C %s" % i)
 | 
			
		||||
            c.save()
 | 
			
		||||
 | 
			
		||||
            members += [a, b, c]
 | 
			
		||||
@@ -819,7 +825,7 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
            self.assertEqual(q, 4)
 | 
			
		||||
 | 
			
		||||
            for k, m in iteritems(group_obj.members):
 | 
			
		||||
                self.assertIn('User', m.__class__.__name__)
 | 
			
		||||
                self.assertIn("User", m.__class__.__name__)
 | 
			
		||||
 | 
			
		||||
        # Document select_related
 | 
			
		||||
        with query_counter() as q:
 | 
			
		||||
@@ -835,7 +841,7 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
            self.assertEqual(q, 4)
 | 
			
		||||
 | 
			
		||||
            for k, m in iteritems(group_obj.members):
 | 
			
		||||
                self.assertIn('User', m.__class__.__name__)
 | 
			
		||||
                self.assertIn("User", m.__class__.__name__)
 | 
			
		||||
 | 
			
		||||
        # Queryset select_related
 | 
			
		||||
        with query_counter() as q:
 | 
			
		||||
@@ -852,7 +858,7 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
                self.assertEqual(q, 4)
 | 
			
		||||
 | 
			
		||||
                for k, m in iteritems(group_obj.members):
 | 
			
		||||
                    self.assertIn('User', m.__class__.__name__)
 | 
			
		||||
                    self.assertIn("User", m.__class__.__name__)
 | 
			
		||||
 | 
			
		||||
        Group.objects.delete()
 | 
			
		||||
        Group().save()
 | 
			
		||||
@@ -873,10 +879,9 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
        Group.drop_collection()
 | 
			
		||||
 | 
			
		||||
    def test_dict_field_no_field_inheritance(self):
 | 
			
		||||
 | 
			
		||||
        class UserA(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
            meta = {'allow_inheritance': False}
 | 
			
		||||
            meta = {"allow_inheritance": False}
 | 
			
		||||
 | 
			
		||||
        class Group(Document):
 | 
			
		||||
            members = DictField()
 | 
			
		||||
@@ -886,7 +891,7 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
        members = []
 | 
			
		||||
        for i in range(1, 51):
 | 
			
		||||
            a = UserA(name='User A %s' % i)
 | 
			
		||||
            a = UserA(name="User A %s" % i)
 | 
			
		||||
            a.save()
 | 
			
		||||
 | 
			
		||||
            members += [a]
 | 
			
		||||
@@ -949,7 +954,6 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
        Group.drop_collection()
 | 
			
		||||
 | 
			
		||||
    def test_generic_reference_map_field(self):
 | 
			
		||||
 | 
			
		||||
        class UserA(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
@@ -969,13 +973,13 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
        members = []
 | 
			
		||||
        for i in range(1, 51):
 | 
			
		||||
            a = UserA(name='User A %s' % i)
 | 
			
		||||
            a = UserA(name="User A %s" % i)
 | 
			
		||||
            a.save()
 | 
			
		||||
 | 
			
		||||
            b = UserB(name='User B %s' % i)
 | 
			
		||||
            b = UserB(name="User B %s" % i)
 | 
			
		||||
            b.save()
 | 
			
		||||
 | 
			
		||||
            c = UserC(name='User C %s' % i)
 | 
			
		||||
            c = UserC(name="User C %s" % i)
 | 
			
		||||
            c.save()
 | 
			
		||||
 | 
			
		||||
            members += [a, b, c]
 | 
			
		||||
@@ -998,7 +1002,7 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
            self.assertEqual(q, 4)
 | 
			
		||||
 | 
			
		||||
            for k, m in iteritems(group_obj.members):
 | 
			
		||||
                self.assertIn('User', m.__class__.__name__)
 | 
			
		||||
                self.assertIn("User", m.__class__.__name__)
 | 
			
		||||
 | 
			
		||||
        # Document select_related
 | 
			
		||||
        with query_counter() as q:
 | 
			
		||||
@@ -1014,7 +1018,7 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
            self.assertEqual(q, 4)
 | 
			
		||||
 | 
			
		||||
            for k, m in iteritems(group_obj.members):
 | 
			
		||||
                self.assertIn('User', m.__class__.__name__)
 | 
			
		||||
                self.assertIn("User", m.__class__.__name__)
 | 
			
		||||
 | 
			
		||||
        # Queryset select_related
 | 
			
		||||
        with query_counter() as q:
 | 
			
		||||
@@ -1031,7 +1035,7 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
                self.assertEqual(q, 4)
 | 
			
		||||
 | 
			
		||||
                for k, m in iteritems(group_obj.members):
 | 
			
		||||
                    self.assertIn('User', m.__class__.__name__)
 | 
			
		||||
                    self.assertIn("User", m.__class__.__name__)
 | 
			
		||||
 | 
			
		||||
        Group.objects.delete()
 | 
			
		||||
        Group().save()
 | 
			
		||||
@@ -1051,7 +1055,6 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
        Group.drop_collection()
 | 
			
		||||
 | 
			
		||||
    def test_multidirectional_lists(self):
 | 
			
		||||
 | 
			
		||||
        class Asset(Document):
 | 
			
		||||
            name = StringField(max_length=250, required=True)
 | 
			
		||||
            path = StringField()
 | 
			
		||||
@@ -1062,10 +1065,10 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
        Asset.drop_collection()
 | 
			
		||||
 | 
			
		||||
        root = Asset(name='', path="/", title="Site Root")
 | 
			
		||||
        root = Asset(name="", path="/", title="Site Root")
 | 
			
		||||
        root.save()
 | 
			
		||||
 | 
			
		||||
        company = Asset(name='company', title='Company', parent=root, parents=[root])
 | 
			
		||||
        company = Asset(name="company", title="Company", parent=root, parents=[root])
 | 
			
		||||
        company.save()
 | 
			
		||||
 | 
			
		||||
        root.children = [company]
 | 
			
		||||
@@ -1076,7 +1079,6 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
        self.assertEqual(company.parents, [root])
 | 
			
		||||
 | 
			
		||||
    def test_dict_in_dbref_instance(self):
 | 
			
		||||
 | 
			
		||||
        class Person(Document):
 | 
			
		||||
            name = StringField(max_length=250, required=True)
 | 
			
		||||
 | 
			
		||||
@@ -1087,34 +1089,35 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
        Person.drop_collection()
 | 
			
		||||
        Room.drop_collection()
 | 
			
		||||
 | 
			
		||||
        bob = Person.objects.create(name='Bob')
 | 
			
		||||
        bob = Person.objects.create(name="Bob")
 | 
			
		||||
        bob.save()
 | 
			
		||||
        sarah = Person.objects.create(name='Sarah')
 | 
			
		||||
        sarah = Person.objects.create(name="Sarah")
 | 
			
		||||
        sarah.save()
 | 
			
		||||
 | 
			
		||||
        room_101 = Room.objects.create(number="101")
 | 
			
		||||
        room_101.staffs_with_position = [
 | 
			
		||||
            {'position_key': 'window', 'staff': sarah},
 | 
			
		||||
            {'position_key': 'door', 'staff': bob.to_dbref()}]
 | 
			
		||||
            {"position_key": "window", "staff": sarah},
 | 
			
		||||
            {"position_key": "door", "staff": bob.to_dbref()},
 | 
			
		||||
        ]
 | 
			
		||||
        room_101.save()
 | 
			
		||||
 | 
			
		||||
        room = Room.objects.first().select_related()
 | 
			
		||||
        self.assertEqual(room.staffs_with_position[0]['staff'], sarah)
 | 
			
		||||
        self.assertEqual(room.staffs_with_position[1]['staff'], bob)
 | 
			
		||||
        self.assertEqual(room.staffs_with_position[0]["staff"], sarah)
 | 
			
		||||
        self.assertEqual(room.staffs_with_position[1]["staff"], bob)
 | 
			
		||||
 | 
			
		||||
    def test_document_reload_no_inheritance(self):
 | 
			
		||||
        class Foo(Document):
 | 
			
		||||
            meta = {'allow_inheritance': False}
 | 
			
		||||
            bar = ReferenceField('Bar')
 | 
			
		||||
            baz = ReferenceField('Baz')
 | 
			
		||||
            meta = {"allow_inheritance": False}
 | 
			
		||||
            bar = ReferenceField("Bar")
 | 
			
		||||
            baz = ReferenceField("Baz")
 | 
			
		||||
 | 
			
		||||
        class Bar(Document):
 | 
			
		||||
            meta = {'allow_inheritance': False}
 | 
			
		||||
            msg = StringField(required=True, default='Blammo!')
 | 
			
		||||
            meta = {"allow_inheritance": False}
 | 
			
		||||
            msg = StringField(required=True, default="Blammo!")
 | 
			
		||||
 | 
			
		||||
        class Baz(Document):
 | 
			
		||||
            meta = {'allow_inheritance': False}
 | 
			
		||||
            msg = StringField(required=True, default='Kaboom!')
 | 
			
		||||
            meta = {"allow_inheritance": False}
 | 
			
		||||
            msg = StringField(required=True, default="Kaboom!")
 | 
			
		||||
 | 
			
		||||
        Foo.drop_collection()
 | 
			
		||||
        Bar.drop_collection()
 | 
			
		||||
@@ -1138,11 +1141,14 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
        Ensure reloading a document with multiple similar id
 | 
			
		||||
        in different collections doesn't mix them.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Topic(Document):
 | 
			
		||||
            id = IntField(primary_key=True)
 | 
			
		||||
 | 
			
		||||
        class User(Document):
 | 
			
		||||
            id = IntField(primary_key=True)
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
        class Message(Document):
 | 
			
		||||
            id = IntField(primary_key=True)
 | 
			
		||||
            topic = ReferenceField(Topic)
 | 
			
		||||
@@ -1154,23 +1160,24 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
        # All objects share the same id, but each in a different collection
 | 
			
		||||
        topic = Topic(id=1).save()
 | 
			
		||||
        user = User(id=1, name='user-name').save()
 | 
			
		||||
        user = User(id=1, name="user-name").save()
 | 
			
		||||
        Message(id=1, topic=topic, author=user).save()
 | 
			
		||||
 | 
			
		||||
        concurrent_change_user = User.objects.get(id=1)
 | 
			
		||||
        concurrent_change_user.name = 'new-name'
 | 
			
		||||
        concurrent_change_user.name = "new-name"
 | 
			
		||||
        concurrent_change_user.save()
 | 
			
		||||
        self.assertNotEqual(user.name, 'new-name')
 | 
			
		||||
        self.assertNotEqual(user.name, "new-name")
 | 
			
		||||
 | 
			
		||||
        msg = Message.objects.get(id=1)
 | 
			
		||||
        msg.reload()
 | 
			
		||||
        self.assertEqual(msg.topic, topic)
 | 
			
		||||
        self.assertEqual(msg.author, user)
 | 
			
		||||
        self.assertEqual(msg.author.name, 'new-name')
 | 
			
		||||
        self.assertEqual(msg.author.name, "new-name")
 | 
			
		||||
 | 
			
		||||
    def test_list_lookup_not_checked_in_map(self):
 | 
			
		||||
        """Ensure we dereference list data correctly
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Comment(Document):
 | 
			
		||||
            id = IntField(primary_key=True)
 | 
			
		||||
            text = StringField()
 | 
			
		||||
@@ -1182,8 +1189,8 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
        Comment.drop_collection()
 | 
			
		||||
        Message.drop_collection()
 | 
			
		||||
 | 
			
		||||
        c1 = Comment(id=0, text='zero').save()
 | 
			
		||||
        c2 = Comment(id=1, text='one').save()
 | 
			
		||||
        c1 = Comment(id=0, text="zero").save()
 | 
			
		||||
        c2 = Comment(id=1, text="one").save()
 | 
			
		||||
        Message(id=1, comments=[c1, c2]).save()
 | 
			
		||||
 | 
			
		||||
        msg = Message.objects.get(id=1)
 | 
			
		||||
@@ -1193,6 +1200,7 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
    def test_list_item_dereference_dref_false_save_doesnt_cause_extra_queries(self):
 | 
			
		||||
        """Ensure that DBRef items in ListFields are dereferenced.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class User(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
@@ -1204,7 +1212,7 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
        Group.drop_collection()
 | 
			
		||||
 | 
			
		||||
        for i in range(1, 51):
 | 
			
		||||
            User(name='user %s' % i).save()
 | 
			
		||||
            User(name="user %s" % i).save()
 | 
			
		||||
 | 
			
		||||
        Group(name="Test", members=User.objects).save()
 | 
			
		||||
 | 
			
		||||
@@ -1222,6 +1230,7 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
    def test_list_item_dereference_dref_true_save_doesnt_cause_extra_queries(self):
 | 
			
		||||
        """Ensure that DBRef items in ListFields are dereferenced.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class User(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
@@ -1233,7 +1242,7 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
        Group.drop_collection()
 | 
			
		||||
 | 
			
		||||
        for i in range(1, 51):
 | 
			
		||||
            User(name='user %s' % i).save()
 | 
			
		||||
            User(name="user %s" % i).save()
 | 
			
		||||
 | 
			
		||||
        Group(name="Test", members=User.objects).save()
 | 
			
		||||
 | 
			
		||||
@@ -1249,7 +1258,6 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
            self.assertEqual(q, 2)
 | 
			
		||||
 | 
			
		||||
    def test_generic_reference_save_doesnt_cause_extra_queries(self):
 | 
			
		||||
 | 
			
		||||
        class UserA(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
@@ -1270,9 +1278,9 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
        members = []
 | 
			
		||||
        for i in range(1, 51):
 | 
			
		||||
            a = UserA(name='User A %s' % i).save()
 | 
			
		||||
            b = UserB(name='User B %s' % i).save()
 | 
			
		||||
            c = UserC(name='User C %s' % i).save()
 | 
			
		||||
            a = UserA(name="User A %s" % i).save()
 | 
			
		||||
            b = UserB(name="User B %s" % i).save()
 | 
			
		||||
            c = UserC(name="User C %s" % i).save()
 | 
			
		||||
 | 
			
		||||
            members += [a, b, c]
 | 
			
		||||
 | 
			
		||||
@@ -1292,7 +1300,7 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
    def test_objectid_reference_across_databases(self):
 | 
			
		||||
        # mongoenginetest - Is default connection alias from setUp()
 | 
			
		||||
        # Register Aliases
 | 
			
		||||
        register_connection('testdb-1', 'mongoenginetest2')
 | 
			
		||||
        register_connection("testdb-1", "mongoenginetest2")
 | 
			
		||||
 | 
			
		||||
        class User(Document):
 | 
			
		||||
            name = StringField()
 | 
			
		||||
@@ -1311,16 +1319,17 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
        # Can't use query_counter across databases - so test the _data object
 | 
			
		||||
        book = Book.objects.first()
 | 
			
		||||
        self.assertNotIsInstance(book._data['author'], User)
 | 
			
		||||
        self.assertNotIsInstance(book._data["author"], User)
 | 
			
		||||
 | 
			
		||||
        book.select_related()
 | 
			
		||||
        self.assertIsInstance(book._data['author'], User)
 | 
			
		||||
        self.assertIsInstance(book._data["author"], User)
 | 
			
		||||
 | 
			
		||||
    def test_non_ascii_pk(self):
 | 
			
		||||
        """
 | 
			
		||||
        Ensure that dbref conversion to string does not fail when
 | 
			
		||||
        non-ascii characters are used in primary key
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        class Brand(Document):
 | 
			
		||||
            title = StringField(max_length=255, primary_key=True)
 | 
			
		||||
 | 
			
		||||
@@ -1341,7 +1350,7 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    def test_dereferencing_embedded_listfield_referencefield(self):
 | 
			
		||||
        class Tag(Document):
 | 
			
		||||
            meta = {'collection': 'tags'}
 | 
			
		||||
            meta = {"collection": "tags"}
 | 
			
		||||
            name = StringField()
 | 
			
		||||
 | 
			
		||||
        class Post(EmbeddedDocument):
 | 
			
		||||
@@ -1349,22 +1358,21 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
            tags = ListField(ReferenceField("Tag", dbref=True))
 | 
			
		||||
 | 
			
		||||
        class Page(Document):
 | 
			
		||||
            meta = {'collection': 'pages'}
 | 
			
		||||
            meta = {"collection": "pages"}
 | 
			
		||||
            tags = ListField(ReferenceField("Tag", dbref=True))
 | 
			
		||||
            posts = ListField(EmbeddedDocumentField(Post))
 | 
			
		||||
 | 
			
		||||
        Tag.drop_collection()
 | 
			
		||||
        Page.drop_collection()
 | 
			
		||||
 | 
			
		||||
        tag = Tag(name='test').save()
 | 
			
		||||
        post = Post(body='test body', tags=[tag])
 | 
			
		||||
        tag = Tag(name="test").save()
 | 
			
		||||
        post = Post(body="test body", tags=[tag])
 | 
			
		||||
        Page(tags=[tag], posts=[post]).save()
 | 
			
		||||
 | 
			
		||||
        page = Page.objects.first()
 | 
			
		||||
        self.assertEqual(page.tags[0], page.posts[0].tags[0])
 | 
			
		||||
 | 
			
		||||
    def test_select_related_follows_embedded_referencefields(self):
 | 
			
		||||
 | 
			
		||||
        class Song(Document):
 | 
			
		||||
            title = StringField()
 | 
			
		||||
 | 
			
		||||
@@ -1390,5 +1398,5 @@ class FieldTest(unittest.TestCase):
 | 
			
		||||
            self.assertEqual(q, 2)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    unittest.main()
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,6 @@ READ_PREF = ReadPreference.SECONDARY
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ConnectionTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        mongoengine.connection._connection_settings = {}
 | 
			
		||||
        mongoengine.connection._connections = {}
 | 
			
		||||
@@ -28,9 +27,11 @@ class ConnectionTest(unittest.TestCase):
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            conn = mongoengine.connect(db='mongoenginetest',
 | 
			
		||||
                           host="mongodb://localhost/mongoenginetest?replicaSet=rs",
 | 
			
		||||
                           read_preference=READ_PREF)
 | 
			
		||||
            conn = mongoengine.connect(
 | 
			
		||||
                db="mongoenginetest",
 | 
			
		||||
                host="mongodb://localhost/mongoenginetest?replicaSet=rs",
 | 
			
		||||
                read_preference=READ_PREF,
 | 
			
		||||
            )
 | 
			
		||||
        except MongoEngineConnectionError as e:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
@@ -41,5 +42,5 @@ class ConnectionTest(unittest.TestCase):
 | 
			
		||||
        self.assertEqual(conn.read_preference, READ_PREF)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    unittest.main()
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,7 @@ class SignalTests(unittest.TestCase):
 | 
			
		||||
        return signal_output
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        connect(db='mongoenginetest')
 | 
			
		||||
        connect(db="mongoenginetest")
 | 
			
		||||
 | 
			
		||||
        class Author(Document):
 | 
			
		||||
            # Make the id deterministic for easier testing
 | 
			
		||||
@@ -32,60 +32,63 @@ class SignalTests(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
            @classmethod
 | 
			
		||||
            def pre_init(cls, sender, document, *args, **kwargs):
 | 
			
		||||
                signal_output.append('pre_init signal, %s' % cls.__name__)
 | 
			
		||||
                signal_output.append(kwargs['values'])
 | 
			
		||||
                signal_output.append("pre_init signal, %s" % cls.__name__)
 | 
			
		||||
                signal_output.append(kwargs["values"])
 | 
			
		||||
 | 
			
		||||
            @classmethod
 | 
			
		||||
            def post_init(cls, sender, document, **kwargs):
 | 
			
		||||
                signal_output.append('post_init signal, %s, document._created = %s' % (document, document._created))
 | 
			
		||||
                signal_output.append(
 | 
			
		||||
                    "post_init signal, %s, document._created = %s"
 | 
			
		||||
                    % (document, document._created)
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
            @classmethod
 | 
			
		||||
            def pre_save(cls, sender, document, **kwargs):
 | 
			
		||||
                signal_output.append('pre_save signal, %s' % document)
 | 
			
		||||
                signal_output.append("pre_save signal, %s" % document)
 | 
			
		||||
                signal_output.append(kwargs)
 | 
			
		||||
 | 
			
		||||
            @classmethod
 | 
			
		||||
            def pre_save_post_validation(cls, sender, document, **kwargs):
 | 
			
		||||
                signal_output.append('pre_save_post_validation signal, %s' % document)
 | 
			
		||||
                if kwargs.pop('created', False):
 | 
			
		||||
                    signal_output.append('Is created')
 | 
			
		||||
                signal_output.append("pre_save_post_validation signal, %s" % document)
 | 
			
		||||
                if kwargs.pop("created", False):
 | 
			
		||||
                    signal_output.append("Is created")
 | 
			
		||||
                else:
 | 
			
		||||
                    signal_output.append('Is updated')
 | 
			
		||||
                    signal_output.append("Is updated")
 | 
			
		||||
                signal_output.append(kwargs)
 | 
			
		||||
 | 
			
		||||
            @classmethod
 | 
			
		||||
            def post_save(cls, sender, document, **kwargs):
 | 
			
		||||
                dirty_keys = document._delta()[0].keys() + document._delta()[1].keys()
 | 
			
		||||
                signal_output.append('post_save signal, %s' % document)
 | 
			
		||||
                signal_output.append('post_save dirty keys, %s' % dirty_keys)
 | 
			
		||||
                if kwargs.pop('created', False):
 | 
			
		||||
                    signal_output.append('Is created')
 | 
			
		||||
                signal_output.append("post_save signal, %s" % document)
 | 
			
		||||
                signal_output.append("post_save dirty keys, %s" % dirty_keys)
 | 
			
		||||
                if kwargs.pop("created", False):
 | 
			
		||||
                    signal_output.append("Is created")
 | 
			
		||||
                else:
 | 
			
		||||
                    signal_output.append('Is updated')
 | 
			
		||||
                    signal_output.append("Is updated")
 | 
			
		||||
                signal_output.append(kwargs)
 | 
			
		||||
 | 
			
		||||
            @classmethod
 | 
			
		||||
            def pre_delete(cls, sender, document, **kwargs):
 | 
			
		||||
                signal_output.append('pre_delete signal, %s' % document)
 | 
			
		||||
                signal_output.append("pre_delete signal, %s" % document)
 | 
			
		||||
                signal_output.append(kwargs)
 | 
			
		||||
 | 
			
		||||
            @classmethod
 | 
			
		||||
            def post_delete(cls, sender, document, **kwargs):
 | 
			
		||||
                signal_output.append('post_delete signal, %s' % document)
 | 
			
		||||
                signal_output.append("post_delete signal, %s" % document)
 | 
			
		||||
                signal_output.append(kwargs)
 | 
			
		||||
 | 
			
		||||
            @classmethod
 | 
			
		||||
            def pre_bulk_insert(cls, sender, documents, **kwargs):
 | 
			
		||||
                signal_output.append('pre_bulk_insert signal, %s' % documents)
 | 
			
		||||
                signal_output.append("pre_bulk_insert signal, %s" % documents)
 | 
			
		||||
                signal_output.append(kwargs)
 | 
			
		||||
 | 
			
		||||
            @classmethod
 | 
			
		||||
            def post_bulk_insert(cls, sender, documents, **kwargs):
 | 
			
		||||
                signal_output.append('post_bulk_insert signal, %s' % documents)
 | 
			
		||||
                if kwargs.pop('loaded', False):
 | 
			
		||||
                    signal_output.append('Is loaded')
 | 
			
		||||
                signal_output.append("post_bulk_insert signal, %s" % documents)
 | 
			
		||||
                if kwargs.pop("loaded", False):
 | 
			
		||||
                    signal_output.append("Is loaded")
 | 
			
		||||
                else:
 | 
			
		||||
                    signal_output.append('Not loaded')
 | 
			
		||||
                    signal_output.append("Not loaded")
 | 
			
		||||
                signal_output.append(kwargs)
 | 
			
		||||
 | 
			
		||||
        self.Author = Author
 | 
			
		||||
@@ -101,12 +104,12 @@ class SignalTests(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
            @classmethod
 | 
			
		||||
            def pre_delete(cls, sender, document, **kwargs):
 | 
			
		||||
                signal_output.append('pre_delete signal, %s' % document)
 | 
			
		||||
                signal_output.append("pre_delete signal, %s" % document)
 | 
			
		||||
                signal_output.append(kwargs)
 | 
			
		||||
 | 
			
		||||
            @classmethod
 | 
			
		||||
            def post_delete(cls, sender, document, **kwargs):
 | 
			
		||||
                signal_output.append('post_delete signal, %s' % document)
 | 
			
		||||
                signal_output.append("post_delete signal, %s" % document)
 | 
			
		||||
                signal_output.append(kwargs)
 | 
			
		||||
 | 
			
		||||
        self.Another = Another
 | 
			
		||||
@@ -117,11 +120,11 @@ class SignalTests(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
            @classmethod
 | 
			
		||||
            def post_save(cls, sender, document, **kwargs):
 | 
			
		||||
                if 'created' in kwargs:
 | 
			
		||||
                    if kwargs['created']:
 | 
			
		||||
                        signal_output.append('Is created')
 | 
			
		||||
                if "created" in kwargs:
 | 
			
		||||
                    if kwargs["created"]:
 | 
			
		||||
                        signal_output.append("Is created")
 | 
			
		||||
                    else:
 | 
			
		||||
                        signal_output.append('Is updated')
 | 
			
		||||
                        signal_output.append("Is updated")
 | 
			
		||||
 | 
			
		||||
        self.ExplicitId = ExplicitId
 | 
			
		||||
        ExplicitId.drop_collection()
 | 
			
		||||
@@ -136,9 +139,13 @@ class SignalTests(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
            @classmethod
 | 
			
		||||
            def pre_bulk_insert(cls, sender, documents, **kwargs):
 | 
			
		||||
                signal_output.append('pre_bulk_insert signal, %s' %
 | 
			
		||||
                                     [(doc, {'active': documents[n].active})
 | 
			
		||||
                                      for n, doc in enumerate(documents)])
 | 
			
		||||
                signal_output.append(
 | 
			
		||||
                    "pre_bulk_insert signal, %s"
 | 
			
		||||
                    % [
 | 
			
		||||
                        (doc, {"active": documents[n].active})
 | 
			
		||||
                        for n, doc in enumerate(documents)
 | 
			
		||||
                    ]
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
                # make changes here, this is just an example -
 | 
			
		||||
                # it could be anything that needs pre-validation or looks-ups before bulk bulk inserting
 | 
			
		||||
@@ -149,13 +156,17 @@ class SignalTests(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
            @classmethod
 | 
			
		||||
            def post_bulk_insert(cls, sender, documents, **kwargs):
 | 
			
		||||
                signal_output.append('post_bulk_insert signal, %s' %
 | 
			
		||||
                                     [(doc, {'active': documents[n].active})
 | 
			
		||||
                                      for n, doc in enumerate(documents)])
 | 
			
		||||
                if kwargs.pop('loaded', False):
 | 
			
		||||
                    signal_output.append('Is loaded')
 | 
			
		||||
                signal_output.append(
 | 
			
		||||
                    "post_bulk_insert signal, %s"
 | 
			
		||||
                    % [
 | 
			
		||||
                        (doc, {"active": documents[n].active})
 | 
			
		||||
                        for n, doc in enumerate(documents)
 | 
			
		||||
                    ]
 | 
			
		||||
                )
 | 
			
		||||
                if kwargs.pop("loaded", False):
 | 
			
		||||
                    signal_output.append("Is loaded")
 | 
			
		||||
                else:
 | 
			
		||||
                    signal_output.append('Not loaded')
 | 
			
		||||
                    signal_output.append("Not loaded")
 | 
			
		||||
                signal_output.append(kwargs)
 | 
			
		||||
 | 
			
		||||
        self.Post = Post
 | 
			
		||||
@@ -178,7 +189,9 @@ class SignalTests(unittest.TestCase):
 | 
			
		||||
        signals.pre_init.connect(Author.pre_init, sender=Author)
 | 
			
		||||
        signals.post_init.connect(Author.post_init, sender=Author)
 | 
			
		||||
        signals.pre_save.connect(Author.pre_save, sender=Author)
 | 
			
		||||
        signals.pre_save_post_validation.connect(Author.pre_save_post_validation, sender=Author)
 | 
			
		||||
        signals.pre_save_post_validation.connect(
 | 
			
		||||
            Author.pre_save_post_validation, sender=Author
 | 
			
		||||
        )
 | 
			
		||||
        signals.post_save.connect(Author.post_save, sender=Author)
 | 
			
		||||
        signals.pre_delete.connect(Author.pre_delete, sender=Author)
 | 
			
		||||
        signals.post_delete.connect(Author.post_delete, sender=Author)
 | 
			
		||||
@@ -199,7 +212,9 @@ class SignalTests(unittest.TestCase):
 | 
			
		||||
        signals.post_delete.disconnect(self.Author.post_delete)
 | 
			
		||||
        signals.pre_delete.disconnect(self.Author.pre_delete)
 | 
			
		||||
        signals.post_save.disconnect(self.Author.post_save)
 | 
			
		||||
        signals.pre_save_post_validation.disconnect(self.Author.pre_save_post_validation)
 | 
			
		||||
        signals.pre_save_post_validation.disconnect(
 | 
			
		||||
            self.Author.pre_save_post_validation
 | 
			
		||||
        )
 | 
			
		||||
        signals.pre_save.disconnect(self.Author.pre_save)
 | 
			
		||||
        signals.pre_bulk_insert.disconnect(self.Author.pre_bulk_insert)
 | 
			
		||||
        signals.post_bulk_insert.disconnect(self.Author.post_bulk_insert)
 | 
			
		||||
@@ -236,203 +251,236 @@ class SignalTests(unittest.TestCase):
 | 
			
		||||
        """ Model saves should throw some signals. """
 | 
			
		||||
 | 
			
		||||
        def create_author():
 | 
			
		||||
            self.Author(name='Bill Shakespeare')
 | 
			
		||||
            self.Author(name="Bill Shakespeare")
 | 
			
		||||
 | 
			
		||||
        def bulk_create_author_with_load():
 | 
			
		||||
            a1 = self.Author(name='Bill Shakespeare')
 | 
			
		||||
            a1 = self.Author(name="Bill Shakespeare")
 | 
			
		||||
            self.Author.objects.insert([a1], load_bulk=True)
 | 
			
		||||
 | 
			
		||||
        def bulk_create_author_without_load():
 | 
			
		||||
            a1 = self.Author(name='Bill Shakespeare')
 | 
			
		||||
            a1 = self.Author(name="Bill Shakespeare")
 | 
			
		||||
            self.Author.objects.insert([a1], load_bulk=False)
 | 
			
		||||
 | 
			
		||||
        def load_existing_author():
 | 
			
		||||
            a  = self.Author(name='Bill Shakespeare')
 | 
			
		||||
            a = self.Author(name="Bill Shakespeare")
 | 
			
		||||
            a.save()
 | 
			
		||||
            self.get_signal_output(lambda: None)  # eliminate signal output
 | 
			
		||||
            a1 = self.Author.objects(name='Bill Shakespeare')[0]
 | 
			
		||||
            a1 = self.Author.objects(name="Bill Shakespeare")[0]
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(self.get_signal_output(create_author), [
 | 
			
		||||
            "pre_init signal, Author",
 | 
			
		||||
            {'name': 'Bill Shakespeare'},
 | 
			
		||||
            "post_init signal, Bill Shakespeare, document._created = True",
 | 
			
		||||
        ])
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            self.get_signal_output(create_author),
 | 
			
		||||
            [
 | 
			
		||||
                "pre_init signal, Author",
 | 
			
		||||
                {"name": "Bill Shakespeare"},
 | 
			
		||||
                "post_init signal, Bill Shakespeare, document._created = True",
 | 
			
		||||
            ],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        a1 = self.Author(name='Bill Shakespeare')
 | 
			
		||||
        self.assertEqual(self.get_signal_output(a1.save), [
 | 
			
		||||
            "pre_save signal, Bill Shakespeare",
 | 
			
		||||
            {},
 | 
			
		||||
            "pre_save_post_validation signal, Bill Shakespeare",
 | 
			
		||||
            "Is created",
 | 
			
		||||
            {},
 | 
			
		||||
            "post_save signal, Bill Shakespeare",
 | 
			
		||||
            "post_save dirty keys, ['name']",
 | 
			
		||||
            "Is created",
 | 
			
		||||
            {}
 | 
			
		||||
        ])
 | 
			
		||||
        a1 = self.Author(name="Bill Shakespeare")
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            self.get_signal_output(a1.save),
 | 
			
		||||
            [
 | 
			
		||||
                "pre_save signal, Bill Shakespeare",
 | 
			
		||||
                {},
 | 
			
		||||
                "pre_save_post_validation signal, Bill Shakespeare",
 | 
			
		||||
                "Is created",
 | 
			
		||||
                {},
 | 
			
		||||
                "post_save signal, Bill Shakespeare",
 | 
			
		||||
                "post_save dirty keys, ['name']",
 | 
			
		||||
                "Is created",
 | 
			
		||||
                {},
 | 
			
		||||
            ],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        a1.reload()
 | 
			
		||||
        a1.name = 'William Shakespeare'
 | 
			
		||||
        self.assertEqual(self.get_signal_output(a1.save), [
 | 
			
		||||
            "pre_save signal, William Shakespeare",
 | 
			
		||||
            {},
 | 
			
		||||
            "pre_save_post_validation signal, William Shakespeare",
 | 
			
		||||
            "Is updated",
 | 
			
		||||
            {},
 | 
			
		||||
            "post_save signal, William Shakespeare",
 | 
			
		||||
            "post_save dirty keys, ['name']",
 | 
			
		||||
            "Is updated",
 | 
			
		||||
            {}
 | 
			
		||||
        ])
 | 
			
		||||
        a1.name = "William Shakespeare"
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            self.get_signal_output(a1.save),
 | 
			
		||||
            [
 | 
			
		||||
                "pre_save signal, William Shakespeare",
 | 
			
		||||
                {},
 | 
			
		||||
                "pre_save_post_validation signal, William Shakespeare",
 | 
			
		||||
                "Is updated",
 | 
			
		||||
                {},
 | 
			
		||||
                "post_save signal, William Shakespeare",
 | 
			
		||||
                "post_save dirty keys, ['name']",
 | 
			
		||||
                "Is updated",
 | 
			
		||||
                {},
 | 
			
		||||
            ],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(self.get_signal_output(a1.delete), [
 | 
			
		||||
            'pre_delete signal, William Shakespeare',
 | 
			
		||||
            {},
 | 
			
		||||
            'post_delete signal, William Shakespeare',
 | 
			
		||||
            {}
 | 
			
		||||
        ])
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            self.get_signal_output(a1.delete),
 | 
			
		||||
            [
 | 
			
		||||
                "pre_delete signal, William Shakespeare",
 | 
			
		||||
                {},
 | 
			
		||||
                "post_delete signal, William Shakespeare",
 | 
			
		||||
                {},
 | 
			
		||||
            ],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(self.get_signal_output(load_existing_author), [
 | 
			
		||||
            "pre_init signal, Author",
 | 
			
		||||
            {'id': 2, 'name': 'Bill Shakespeare'},
 | 
			
		||||
            "post_init signal, Bill Shakespeare, document._created = False"
 | 
			
		||||
        ])
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            self.get_signal_output(load_existing_author),
 | 
			
		||||
            [
 | 
			
		||||
                "pre_init signal, Author",
 | 
			
		||||
                {"id": 2, "name": "Bill Shakespeare"},
 | 
			
		||||
                "post_init signal, Bill Shakespeare, document._created = False",
 | 
			
		||||
            ],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(self.get_signal_output(bulk_create_author_with_load), [
 | 
			
		||||
            'pre_init signal, Author',
 | 
			
		||||
            {'name': 'Bill Shakespeare'},
 | 
			
		||||
            'post_init signal, Bill Shakespeare, document._created = True',
 | 
			
		||||
            'pre_bulk_insert signal, [<Author: Bill Shakespeare>]',
 | 
			
		||||
            {},
 | 
			
		||||
            'pre_init signal, Author',
 | 
			
		||||
            {'id': 3, 'name': 'Bill Shakespeare'},
 | 
			
		||||
            'post_init signal, Bill Shakespeare, document._created = False',
 | 
			
		||||
            'post_bulk_insert signal, [<Author: Bill Shakespeare>]',
 | 
			
		||||
            'Is loaded',
 | 
			
		||||
            {}
 | 
			
		||||
        ])
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            self.get_signal_output(bulk_create_author_with_load),
 | 
			
		||||
            [
 | 
			
		||||
                "pre_init signal, Author",
 | 
			
		||||
                {"name": "Bill Shakespeare"},
 | 
			
		||||
                "post_init signal, Bill Shakespeare, document._created = True",
 | 
			
		||||
                "pre_bulk_insert signal, [<Author: Bill Shakespeare>]",
 | 
			
		||||
                {},
 | 
			
		||||
                "pre_init signal, Author",
 | 
			
		||||
                {"id": 3, "name": "Bill Shakespeare"},
 | 
			
		||||
                "post_init signal, Bill Shakespeare, document._created = False",
 | 
			
		||||
                "post_bulk_insert signal, [<Author: Bill Shakespeare>]",
 | 
			
		||||
                "Is loaded",
 | 
			
		||||
                {},
 | 
			
		||||
            ],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(self.get_signal_output(bulk_create_author_without_load), [
 | 
			
		||||
            "pre_init signal, Author",
 | 
			
		||||
            {'name': 'Bill Shakespeare'},
 | 
			
		||||
            "post_init signal, Bill Shakespeare, document._created = True",
 | 
			
		||||
            "pre_bulk_insert signal, [<Author: Bill Shakespeare>]",
 | 
			
		||||
            {},
 | 
			
		||||
            "post_bulk_insert signal, [<Author: Bill Shakespeare>]",
 | 
			
		||||
            "Not loaded",
 | 
			
		||||
            {}
 | 
			
		||||
        ])
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            self.get_signal_output(bulk_create_author_without_load),
 | 
			
		||||
            [
 | 
			
		||||
                "pre_init signal, Author",
 | 
			
		||||
                {"name": "Bill Shakespeare"},
 | 
			
		||||
                "post_init signal, Bill Shakespeare, document._created = True",
 | 
			
		||||
                "pre_bulk_insert signal, [<Author: Bill Shakespeare>]",
 | 
			
		||||
                {},
 | 
			
		||||
                "post_bulk_insert signal, [<Author: Bill Shakespeare>]",
 | 
			
		||||
                "Not loaded",
 | 
			
		||||
                {},
 | 
			
		||||
            ],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_signal_kwargs(self):
 | 
			
		||||
        """ Make sure signal_kwargs is passed to signals calls. """
 | 
			
		||||
 | 
			
		||||
        def live_and_let_die():
 | 
			
		||||
            a = self.Author(name='Bill Shakespeare')
 | 
			
		||||
            a.save(signal_kwargs={'live': True, 'die': False})
 | 
			
		||||
            a.delete(signal_kwargs={'live': False, 'die': True})
 | 
			
		||||
            a = self.Author(name="Bill Shakespeare")
 | 
			
		||||
            a.save(signal_kwargs={"live": True, "die": False})
 | 
			
		||||
            a.delete(signal_kwargs={"live": False, "die": True})
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(self.get_signal_output(live_and_let_die), [
 | 
			
		||||
            "pre_init signal, Author",
 | 
			
		||||
            {'name': 'Bill Shakespeare'},
 | 
			
		||||
            "post_init signal, Bill Shakespeare, document._created = True",
 | 
			
		||||
            "pre_save signal, Bill Shakespeare",
 | 
			
		||||
            {'die': False, 'live': True},
 | 
			
		||||
            "pre_save_post_validation signal, Bill Shakespeare",
 | 
			
		||||
            "Is created",
 | 
			
		||||
            {'die': False, 'live': True},
 | 
			
		||||
            "post_save signal, Bill Shakespeare",
 | 
			
		||||
            "post_save dirty keys, ['name']",
 | 
			
		||||
            "Is created",
 | 
			
		||||
            {'die': False, 'live': True},
 | 
			
		||||
            'pre_delete signal, Bill Shakespeare',
 | 
			
		||||
            {'die': True, 'live': False},
 | 
			
		||||
            'post_delete signal, Bill Shakespeare',
 | 
			
		||||
            {'die': True, 'live': False}
 | 
			
		||||
        ])
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            self.get_signal_output(live_and_let_die),
 | 
			
		||||
            [
 | 
			
		||||
                "pre_init signal, Author",
 | 
			
		||||
                {"name": "Bill Shakespeare"},
 | 
			
		||||
                "post_init signal, Bill Shakespeare, document._created = True",
 | 
			
		||||
                "pre_save signal, Bill Shakespeare",
 | 
			
		||||
                {"die": False, "live": True},
 | 
			
		||||
                "pre_save_post_validation signal, Bill Shakespeare",
 | 
			
		||||
                "Is created",
 | 
			
		||||
                {"die": False, "live": True},
 | 
			
		||||
                "post_save signal, Bill Shakespeare",
 | 
			
		||||
                "post_save dirty keys, ['name']",
 | 
			
		||||
                "Is created",
 | 
			
		||||
                {"die": False, "live": True},
 | 
			
		||||
                "pre_delete signal, Bill Shakespeare",
 | 
			
		||||
                {"die": True, "live": False},
 | 
			
		||||
                "post_delete signal, Bill Shakespeare",
 | 
			
		||||
                {"die": True, "live": False},
 | 
			
		||||
            ],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        def bulk_create_author():
 | 
			
		||||
            a1 = self.Author(name='Bill Shakespeare')
 | 
			
		||||
            self.Author.objects.insert([a1], signal_kwargs={'key': True})
 | 
			
		||||
            a1 = self.Author(name="Bill Shakespeare")
 | 
			
		||||
            self.Author.objects.insert([a1], signal_kwargs={"key": True})
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(self.get_signal_output(bulk_create_author), [
 | 
			
		||||
            'pre_init signal, Author',
 | 
			
		||||
            {'name': 'Bill Shakespeare'},
 | 
			
		||||
            'post_init signal, Bill Shakespeare, document._created = True',
 | 
			
		||||
            'pre_bulk_insert signal, [<Author: Bill Shakespeare>]',
 | 
			
		||||
            {'key': True},
 | 
			
		||||
            'pre_init signal, Author',
 | 
			
		||||
            {'id': 2, 'name': 'Bill Shakespeare'},
 | 
			
		||||
            'post_init signal, Bill Shakespeare, document._created = False',
 | 
			
		||||
            'post_bulk_insert signal, [<Author: Bill Shakespeare>]',
 | 
			
		||||
            'Is loaded',
 | 
			
		||||
            {'key': True}
 | 
			
		||||
        ])
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            self.get_signal_output(bulk_create_author),
 | 
			
		||||
            [
 | 
			
		||||
                "pre_init signal, Author",
 | 
			
		||||
                {"name": "Bill Shakespeare"},
 | 
			
		||||
                "post_init signal, Bill Shakespeare, document._created = True",
 | 
			
		||||
                "pre_bulk_insert signal, [<Author: Bill Shakespeare>]",
 | 
			
		||||
                {"key": True},
 | 
			
		||||
                "pre_init signal, Author",
 | 
			
		||||
                {"id": 2, "name": "Bill Shakespeare"},
 | 
			
		||||
                "post_init signal, Bill Shakespeare, document._created = False",
 | 
			
		||||
                "post_bulk_insert signal, [<Author: Bill Shakespeare>]",
 | 
			
		||||
                "Is loaded",
 | 
			
		||||
                {"key": True},
 | 
			
		||||
            ],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_queryset_delete_signals(self):
 | 
			
		||||
        """ Queryset delete should throw some signals. """
 | 
			
		||||
 | 
			
		||||
        self.Another(name='Bill Shakespeare').save()
 | 
			
		||||
        self.assertEqual(self.get_signal_output(self.Another.objects.delete), [
 | 
			
		||||
            'pre_delete signal, Bill Shakespeare',
 | 
			
		||||
            {},
 | 
			
		||||
            'post_delete signal, Bill Shakespeare',
 | 
			
		||||
            {}
 | 
			
		||||
        ])
 | 
			
		||||
        self.Another(name="Bill Shakespeare").save()
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            self.get_signal_output(self.Another.objects.delete),
 | 
			
		||||
            [
 | 
			
		||||
                "pre_delete signal, Bill Shakespeare",
 | 
			
		||||
                {},
 | 
			
		||||
                "post_delete signal, Bill Shakespeare",
 | 
			
		||||
                {},
 | 
			
		||||
            ],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_signals_with_explicit_doc_ids(self):
 | 
			
		||||
        """ Model saves must have a created flag the first time."""
 | 
			
		||||
        ei = self.ExplicitId(id=123)
 | 
			
		||||
        # post save must received the created flag, even if there's already
 | 
			
		||||
        # an object id present
 | 
			
		||||
        self.assertEqual(self.get_signal_output(ei.save), ['Is created'])
 | 
			
		||||
        self.assertEqual(self.get_signal_output(ei.save), ["Is created"])
 | 
			
		||||
        # second time, it must be an update
 | 
			
		||||
        self.assertEqual(self.get_signal_output(ei.save), ['Is updated'])
 | 
			
		||||
        self.assertEqual(self.get_signal_output(ei.save), ["Is updated"])
 | 
			
		||||
 | 
			
		||||
    def test_signals_with_switch_collection(self):
 | 
			
		||||
        ei = self.ExplicitId(id=123)
 | 
			
		||||
        ei.switch_collection("explicit__1")
 | 
			
		||||
        self.assertEqual(self.get_signal_output(ei.save), ['Is created'])
 | 
			
		||||
        self.assertEqual(self.get_signal_output(ei.save), ["Is created"])
 | 
			
		||||
        ei.switch_collection("explicit__1")
 | 
			
		||||
        self.assertEqual(self.get_signal_output(ei.save), ['Is updated'])
 | 
			
		||||
        self.assertEqual(self.get_signal_output(ei.save), ["Is updated"])
 | 
			
		||||
 | 
			
		||||
        ei.switch_collection("explicit__1", keep_created=False)
 | 
			
		||||
        self.assertEqual(self.get_signal_output(ei.save), ['Is created'])
 | 
			
		||||
        self.assertEqual(self.get_signal_output(ei.save), ["Is created"])
 | 
			
		||||
        ei.switch_collection("explicit__1", keep_created=False)
 | 
			
		||||
        self.assertEqual(self.get_signal_output(ei.save), ['Is created'])
 | 
			
		||||
        self.assertEqual(self.get_signal_output(ei.save), ["Is created"])
 | 
			
		||||
 | 
			
		||||
    def test_signals_with_switch_db(self):
 | 
			
		||||
        connect('mongoenginetest')
 | 
			
		||||
        register_connection('testdb-1', 'mongoenginetest2')
 | 
			
		||||
        connect("mongoenginetest")
 | 
			
		||||
        register_connection("testdb-1", "mongoenginetest2")
 | 
			
		||||
 | 
			
		||||
        ei = self.ExplicitId(id=123)
 | 
			
		||||
        ei.switch_db("testdb-1")
 | 
			
		||||
        self.assertEqual(self.get_signal_output(ei.save), ['Is created'])
 | 
			
		||||
        self.assertEqual(self.get_signal_output(ei.save), ["Is created"])
 | 
			
		||||
        ei.switch_db("testdb-1")
 | 
			
		||||
        self.assertEqual(self.get_signal_output(ei.save), ['Is updated'])
 | 
			
		||||
        self.assertEqual(self.get_signal_output(ei.save), ["Is updated"])
 | 
			
		||||
 | 
			
		||||
        ei.switch_db("testdb-1", keep_created=False)
 | 
			
		||||
        self.assertEqual(self.get_signal_output(ei.save), ['Is created'])
 | 
			
		||||
        self.assertEqual(self.get_signal_output(ei.save), ["Is created"])
 | 
			
		||||
        ei.switch_db("testdb-1", keep_created=False)
 | 
			
		||||
        self.assertEqual(self.get_signal_output(ei.save), ['Is created'])
 | 
			
		||||
        self.assertEqual(self.get_signal_output(ei.save), ["Is created"])
 | 
			
		||||
 | 
			
		||||
    def test_signals_bulk_insert(self):
 | 
			
		||||
        def bulk_set_active_post():
 | 
			
		||||
            posts = [
 | 
			
		||||
                self.Post(title='Post 1'),
 | 
			
		||||
                self.Post(title='Post 2'),
 | 
			
		||||
                self.Post(title='Post 3')
 | 
			
		||||
                self.Post(title="Post 1"),
 | 
			
		||||
                self.Post(title="Post 2"),
 | 
			
		||||
                self.Post(title="Post 3"),
 | 
			
		||||
            ]
 | 
			
		||||
            self.Post.objects.insert(posts)
 | 
			
		||||
 | 
			
		||||
        results = self.get_signal_output(bulk_set_active_post)
 | 
			
		||||
        self.assertEqual(results, [
 | 
			
		||||
            "pre_bulk_insert signal, [(<Post: Post 1>, {'active': False}), (<Post: Post 2>, {'active': False}), (<Post: Post 3>, {'active': False})]",
 | 
			
		||||
            {},
 | 
			
		||||
            "post_bulk_insert signal, [(<Post: Post 1>, {'active': True}), (<Post: Post 2>, {'active': True}), (<Post: Post 3>, {'active': True})]",
 | 
			
		||||
            'Is loaded',
 | 
			
		||||
            {}
 | 
			
		||||
        ])
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            results,
 | 
			
		||||
            [
 | 
			
		||||
                "pre_bulk_insert signal, [(<Post: Post 1>, {'active': False}), (<Post: Post 2>, {'active': False}), (<Post: Post 3>, {'active': False})]",
 | 
			
		||||
                {},
 | 
			
		||||
                "post_bulk_insert signal, [(<Post: Post 1>, {'active': True}), (<Post: Post 2>, {'active': True}), (<Post: Post 3>, {'active': True})]",
 | 
			
		||||
                "Is loaded",
 | 
			
		||||
                {},
 | 
			
		||||
            ],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    unittest.main()
 | 
			
		||||
 
 | 
			
		||||
@@ -7,32 +7,33 @@ signal_output = []
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LazyRegexCompilerTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    def test_lazy_regex_compiler_verify_laziness_of_descriptor(self):
 | 
			
		||||
        class UserEmail(object):
 | 
			
		||||
            EMAIL_REGEX = LazyRegexCompiler('@', flags=32)
 | 
			
		||||
            EMAIL_REGEX = LazyRegexCompiler("@", flags=32)
 | 
			
		||||
 | 
			
		||||
        descriptor = UserEmail.__dict__['EMAIL_REGEX']
 | 
			
		||||
        descriptor = UserEmail.__dict__["EMAIL_REGEX"]
 | 
			
		||||
        self.assertIsNone(descriptor._compiled_regex)
 | 
			
		||||
 | 
			
		||||
        regex = UserEmail.EMAIL_REGEX
 | 
			
		||||
        self.assertEqual(regex, re.compile('@', flags=32))
 | 
			
		||||
        self.assertEqual(regex.search('user@domain.com').group(), '@')
 | 
			
		||||
        self.assertEqual(regex, re.compile("@", flags=32))
 | 
			
		||||
        self.assertEqual(regex.search("user@domain.com").group(), "@")
 | 
			
		||||
 | 
			
		||||
        user_email = UserEmail()
 | 
			
		||||
        self.assertIs(user_email.EMAIL_REGEX, UserEmail.EMAIL_REGEX)
 | 
			
		||||
 | 
			
		||||
    def test_lazy_regex_compiler_verify_cannot_set_descriptor_on_instance(self):
 | 
			
		||||
        class UserEmail(object):
 | 
			
		||||
            EMAIL_REGEX = LazyRegexCompiler('@')
 | 
			
		||||
            EMAIL_REGEX = LazyRegexCompiler("@")
 | 
			
		||||
 | 
			
		||||
        user_email = UserEmail()
 | 
			
		||||
        with self.assertRaises(AttributeError):
 | 
			
		||||
            user_email.EMAIL_REGEX = re.compile('@')
 | 
			
		||||
            user_email.EMAIL_REGEX = re.compile("@")
 | 
			
		||||
 | 
			
		||||
    def test_lazy_regex_compiler_verify_can_override_class_attr(self):
 | 
			
		||||
        class UserEmail(object):
 | 
			
		||||
            EMAIL_REGEX = LazyRegexCompiler('@')
 | 
			
		||||
            EMAIL_REGEX = LazyRegexCompiler("@")
 | 
			
		||||
 | 
			
		||||
        UserEmail.EMAIL_REGEX = re.compile('cookies')
 | 
			
		||||
        self.assertEqual(UserEmail.EMAIL_REGEX.search('Cake & cookies').group(), 'cookies')
 | 
			
		||||
        UserEmail.EMAIL_REGEX = re.compile("cookies")
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            UserEmail.EMAIL_REGEX.search("Cake & cookies").group(), "cookies"
 | 
			
		||||
        )
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@ from mongoengine.connection import get_db, disconnect_all
 | 
			
		||||
from mongoengine.mongodb_support import get_mongodb_version
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
MONGO_TEST_DB = 'mongoenginetest'   # standard name for the test database
 | 
			
		||||
MONGO_TEST_DB = "mongoenginetest"  # standard name for the test database
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MongoDBTestCase(unittest.TestCase):
 | 
			
		||||
@@ -53,12 +53,15 @@ def _decorated_with_ver_requirement(func, mongo_version_req, oper):
 | 
			
		||||
    :param mongo_version_req: The mongodb version requirement (tuple(int, int))
 | 
			
		||||
    :param oper: The operator to apply (e.g: operator.ge)
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def _inner(*args, **kwargs):
 | 
			
		||||
        mongodb_v = get_mongodb_version()
 | 
			
		||||
        if oper(mongodb_v, mongo_version_req):
 | 
			
		||||
            return func(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
        raise SkipTest('Needs MongoDB v{}+'.format('.'.join(str(n) for n in mongo_version_req)))
 | 
			
		||||
        raise SkipTest(
 | 
			
		||||
            "Needs MongoDB v{}+".format(".".join(str(n) for n in mongo_version_req))
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    _inner.__name__ = func.__name__
 | 
			
		||||
    _inner.__doc__ = func.__doc__
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user