Compare commits
78 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
735e043ff6 | ||
|
6e7f2b73cf | ||
|
d645ce9745 | ||
|
7c08c140da | ||
|
81d402dc17 | ||
|
966fa12358 | ||
|
87792e1921 | ||
|
4c8296acc6 | ||
|
9989da07ed | ||
|
1c5e6a3425 | ||
|
eedf908770 | ||
|
5c9ef41403 | ||
|
0bf2ad5b67 | ||
|
a0e3f382cd | ||
|
f09c39b5d7 | ||
|
89c67bf259 | ||
|
ea666d4607 | ||
|
b8af154439 | ||
|
f594ece32a | ||
|
03beb6852a | ||
|
ab9e9a3329 | ||
|
a4b09344af | ||
|
8cb8aa392c | ||
|
3255519792 | ||
|
7e64bb2503 | ||
|
86a78402c3 | ||
|
ba276452fb | ||
|
4ffa8d0124 | ||
|
4bc5082681 | ||
|
0e3c34e1da | ||
|
658b3784ae | ||
|
0526f577ff | ||
|
bb1b9bc1d3 | ||
|
b1eeb77ddc | ||
|
999d4a7676 | ||
|
1b80193aac | ||
|
be8d39a48c | ||
|
a2f3d70f28 | ||
|
676a7bf712 | ||
|
e990a6c70c | ||
|
90fa0f6c4a | ||
|
22010d7d95 | ||
|
66279bd90f | ||
|
19da228855 | ||
|
9e67941bad | ||
|
0454fc74e9 | ||
|
2f6b1c7611 | ||
|
f00bed6058 | ||
|
529c522594 | ||
|
2bb9493fcf | ||
|
839ed8a64a | ||
|
017a31ffd0 | ||
|
83b961c84d | ||
|
fa07423ca5 | ||
|
dd4af2df81 | ||
|
44bd8cb85b | ||
|
52d80ac23c | ||
|
43a5d73e14 | ||
|
abc764951d | ||
|
0575abab23 | ||
|
9eebcf7beb | ||
|
ed74477150 | ||
|
dd023edc0f | ||
|
f45d4d781d | ||
|
c95652d6a8 | ||
|
97b37f75d3 | ||
|
95dae48778 | ||
|
73635033bd | ||
|
c1619d2a62 | ||
|
b87ef982f6 | ||
|
91aa90ad4a | ||
|
d1add62a06 | ||
|
c419f3379a | ||
|
69d57209f7 | ||
|
7ca81d6fb8 | ||
|
8a046bfa5d | ||
|
3628a7653c | ||
|
1a4533a9cf |
@@ -1,12 +1,15 @@
|
||||
# http://travis-ci.org/#!/MongoEngine/mongoengine
|
||||
language: python
|
||||
python:
|
||||
- 2.5
|
||||
- 2.6
|
||||
- 2.7
|
||||
- 3.1
|
||||
- 3.2
|
||||
install:
|
||||
- sudo apt-get install zlib1g zlib1g-dev
|
||||
- sudo ln -s /usr/lib/i386-linux-gnu/libz.so /usr/lib/
|
||||
- pip install PIL --use-mirrors ; true
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == '2.'* ]]; then sudo apt-get install zlib1g zlib1g-dev; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == '2.'* ]]; then sudo ln -s /usr/lib/i386-linux-gnu/libz.so /usr/lib/; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == '2.'* ]]; then pip install PIL --use-mirrors ; true; fi
|
||||
- python setup.py install
|
||||
script:
|
||||
- python setup.py test
|
6
AUTHORS
6
AUTHORS
@@ -8,6 +8,7 @@ Florian Schlachter <flori@n-schlachter.de>
|
||||
Steve Challis <steve@stevechallis.com>
|
||||
Wilson Júnior <wilsonpjunior@gmail.com>
|
||||
Dan Crosta https://github.com/dcrosta
|
||||
Laine Herron https://github.com/LaineHerron
|
||||
|
||||
CONTRIBUTORS
|
||||
|
||||
@@ -116,3 +117,8 @@ that much better:
|
||||
* Thomas Steinacher
|
||||
* Tommi Komulainen
|
||||
* Peter Landry
|
||||
* biszkoptwielki
|
||||
* Anton Kolechkin
|
||||
* Sergey Nikitin
|
||||
* psychogenic
|
||||
* Stefan Wójcik
|
||||
|
49
benchmark.py
49
benchmark.py
@@ -28,47 +28,64 @@ def main():
|
||||
|
||||
----------------------------------------------------------------------------------------------------
|
||||
Creating 10000 dictionaries - Pymongo
|
||||
1.1141769886
|
||||
3.86744189262
|
||||
----------------------------------------------------------------------------------------------------
|
||||
Creating 10000 dictionaries - MongoEngine
|
||||
2.37724113464
|
||||
6.23374891281
|
||||
----------------------------------------------------------------------------------------------------
|
||||
Creating 10000 dictionaries - MongoEngine, safe=False, validate=False
|
||||
1.92479610443
|
||||
5.33027005196
|
||||
----------------------------------------------------------------------------------------------------
|
||||
Creating 10000 dictionaries - MongoEngine, safe=False, validate=False, cascade=False
|
||||
pass - No Cascade
|
||||
|
||||
0.5.X
|
||||
----------------------------------------------------------------------------------------------------
|
||||
Creating 10000 dictionaries - Pymongo
|
||||
1.10552310944
|
||||
3.89597702026
|
||||
----------------------------------------------------------------------------------------------------
|
||||
Creating 10000 dictionaries - MongoEngine
|
||||
16.5169169903
|
||||
21.7735359669
|
||||
----------------------------------------------------------------------------------------------------
|
||||
Creating 10000 dictionaries - MongoEngine, safe=False, validate=False
|
||||
14.9446101189
|
||||
19.8670389652
|
||||
----------------------------------------------------------------------------------------------------
|
||||
Creating 10000 dictionaries - MongoEngine, safe=False, validate=False, cascade=False
|
||||
14.912801981
|
||||
----------------------------------------------------------------------------------------------------
|
||||
Creating 10000 dictionaries - MongoEngine, force=True
|
||||
14.9617750645
|
||||
pass - No Cascade
|
||||
|
||||
Performance
|
||||
0.6.X
|
||||
----------------------------------------------------------------------------------------------------
|
||||
Creating 10000 dictionaries - Pymongo
|
||||
1.10072994232
|
||||
3.81559205055
|
||||
----------------------------------------------------------------------------------------------------
|
||||
Creating 10000 dictionaries - MongoEngine
|
||||
5.27341103554
|
||||
10.0446798801
|
||||
----------------------------------------------------------------------------------------------------
|
||||
Creating 10000 dictionaries - MongoEngine, safe=False, validate=False
|
||||
4.49365401268
|
||||
9.51354718208
|
||||
----------------------------------------------------------------------------------------------------
|
||||
Creating 10000 dictionaries - MongoEngine, safe=False, validate=False, cascade=False
|
||||
4.43459296227
|
||||
9.02567505836
|
||||
----------------------------------------------------------------------------------------------------
|
||||
Creating 10000 dictionaries - MongoEngine, force=True
|
||||
4.40114378929
|
||||
8.44933390617
|
||||
|
||||
0.7.X
|
||||
----------------------------------------------------------------------------------------------------
|
||||
Creating 10000 dictionaries - Pymongo
|
||||
3.78801012039
|
||||
----------------------------------------------------------------------------------------------------
|
||||
Creating 10000 dictionaries - MongoEngine
|
||||
9.73050498962
|
||||
----------------------------------------------------------------------------------------------------
|
||||
Creating 10000 dictionaries - MongoEngine, safe=False, validate=False
|
||||
8.33456707001
|
||||
----------------------------------------------------------------------------------------------------
|
||||
Creating 10000 dictionaries - MongoEngine, safe=False, validate=False, cascade=False
|
||||
8.37778115273
|
||||
----------------------------------------------------------------------------------------------------
|
||||
Creating 10000 dictionaries - MongoEngine, force=True
|
||||
8.36906409264
|
||||
"""
|
||||
|
||||
setup = """
|
||||
|
@@ -2,6 +2,25 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
Changes in 0.7.X
|
||||
=================
|
||||
- Added NotUniqueError for duplicate keys (MongoEngine/mongoengine#62)
|
||||
- Added custom collection / sequence naming for SequenceFields (MongoEngine/mongoengine#92)
|
||||
- Fixed UnboundLocalError in composite index with pk field (MongoEngine/mongoengine#88)
|
||||
- Updated ReferenceField's to optionally store ObjectId strings
|
||||
this will become the default in 0.8 (MongoEngine/mongoengine#89)
|
||||
- Added FutureWarning - save will default to `cascade=False` in 0.8
|
||||
- Added example of indexing embedded document fields (MongoEngine/mongoengine#75)
|
||||
- Fixed ImageField resizing when forcing size (MongoEngine/mongoengine#80)
|
||||
- Add flexibility for fields handling bad data (MongoEngine/mongoengine#78)
|
||||
- Embedded Documents no longer handle meta definitions
|
||||
- Use weakref proxies in base lists / dicts (MongoEngine/mongoengine#74)
|
||||
- Improved queryset filtering (hmarr/mongoengine#554)
|
||||
- Fixed Dynamic Documents and Embedded Documents (hmarr/mongoengine#561)
|
||||
- Fixed abstract classes and shard keys (MongoEngine/mongoengine#64)
|
||||
- Fixed Python 2.5 support
|
||||
- Added Python 3 support (thanks to Laine Heron)
|
||||
|
||||
Changes in 0.6.20
|
||||
=================
|
||||
- Added support for distinct and db_alias (MongoEngine/mongoengine#59)
|
||||
@@ -86,7 +105,7 @@ Changes in 0.6.8
|
||||
================
|
||||
- Fixed FileField losing reference when no default set
|
||||
- Removed possible race condition from FileField (grid_file)
|
||||
- Added assignment to save, can now do: b = MyDoc(**kwargs).save()
|
||||
- Added assignment to save, can now do: `b = MyDoc(**kwargs).save()`
|
||||
- Added support for pull operations on nested EmbeddedDocuments
|
||||
- Added support for choices with GenericReferenceFields
|
||||
- Added support for choices with GenericEmbeddedDocumentFields
|
||||
|
@@ -467,11 +467,16 @@ If a dictionary is passed then the following options are available:
|
||||
:attr:`unique` (Default: False)
|
||||
Whether the index should be sparse.
|
||||
|
||||
.. note ::
|
||||
|
||||
To index embedded files / dictionary fields use 'dot' notation eg:
|
||||
`rank.title`
|
||||
|
||||
.. warning::
|
||||
|
||||
|
||||
Inheritance adds extra indices.
|
||||
If don't need inheritance for a document turn inheritance off - see :ref:`document-inheritance`.
|
||||
Inheritance adds extra indices.
|
||||
If don't need inheritance for a document turn inheritance off -
|
||||
see :ref:`document-inheritance`.
|
||||
|
||||
|
||||
Geospatial indexes
|
||||
|
@@ -2,18 +2,79 @@
|
||||
Upgrading
|
||||
=========
|
||||
|
||||
0.6 to 0.7
|
||||
==========
|
||||
|
||||
Cascade saves
|
||||
-------------
|
||||
|
||||
Saves will raise a `FutureWarning` if they cascade and cascade hasn't been set
|
||||
to True. This is because in 0.8 it will default to False. If you require
|
||||
cascading saves then either set it in the `meta` or pass
|
||||
via `save` eg ::
|
||||
|
||||
# At the class level:
|
||||
class Person(Document):
|
||||
meta = {'cascade': True}
|
||||
|
||||
# Or in code:
|
||||
my_document.save(cascade=True)
|
||||
|
||||
.. note ::
|
||||
Remember: cascading saves **do not** cascade through lists.
|
||||
|
||||
ReferenceFields
|
||||
---------------
|
||||
|
||||
ReferenceFields now can store references as ObjectId strings instead of DBRefs.
|
||||
This will become the default in 0.8 and if `dbref` is not set a `FutureWarning`
|
||||
will be raised.
|
||||
|
||||
|
||||
To explicitly continue to use DBRefs change the `dbref` flag
|
||||
to True ::
|
||||
|
||||
class Person(Document):
|
||||
groups = ListField(ReferenceField(Group, dbref=True))
|
||||
|
||||
To migrate to using strings instead of DBRefs you will have to manually
|
||||
migrate ::
|
||||
|
||||
# Step 1 - Migrate the model definition
|
||||
class Group(Document):
|
||||
author = ReferenceField(User, dbref=False)
|
||||
members = ListField(ReferenceField(User, dbref=False))
|
||||
|
||||
# Step 2 - Migrate the data
|
||||
for g in Group.objects():
|
||||
g.author = g.author
|
||||
g.members = g.members
|
||||
g.save()
|
||||
|
||||
|
||||
item_frequencies
|
||||
----------------
|
||||
|
||||
In the 0.6 series we added support for null / zero / false values in
|
||||
item_frequencies. A side effect was to return keys in the value they are
|
||||
stored in rather than as string representations. Your code may need to be
|
||||
updated to handle native types rather than strings keys for the results of
|
||||
item frequency queries.
|
||||
|
||||
0.5 to 0.6
|
||||
==========
|
||||
|
||||
Embedded Documents - if you had a `pk` field you will have to rename it from `_id`
|
||||
to `pk` as pk is no longer a property of Embedded Documents.
|
||||
Embedded Documents - if you had a `pk` field you will have to rename it from
|
||||
`_id` to `pk` as pk is no longer a property of Embedded Documents.
|
||||
|
||||
Reverse Delete Rules in Embedded Documents, MapFields and DictFields now throw
|
||||
an InvalidDocument error as they aren't currently supported.
|
||||
|
||||
Document._get_subclasses - Is no longer used and the class method has been removed.
|
||||
Document._get_subclasses - Is no longer used and the class method has been
|
||||
removed.
|
||||
|
||||
Document.objects.with_id - now raises an InvalidQueryError if used with a filter.
|
||||
Document.objects.with_id - now raises an InvalidQueryError if used with a
|
||||
filter.
|
||||
|
||||
FutureWarning - A future warning has been added to all inherited classes that
|
||||
don't define `allow_inheritance` in their meta.
|
||||
@@ -37,11 +98,11 @@ human-readable name for the option.
|
||||
PyMongo / MongoDB
|
||||
-----------------
|
||||
|
||||
map reduce now requires pymongo 1.11+- The pymongo merge_output and reduce_output
|
||||
parameters, have been depreciated.
|
||||
map reduce now requires pymongo 1.11+- The pymongo `merge_output` and
|
||||
`reduce_output` parameters, have been depreciated.
|
||||
|
||||
More methods now use map_reduce as db.eval is not supported for sharding as such
|
||||
the following have been changed:
|
||||
More methods now use map_reduce as db.eval is not supported for sharding as
|
||||
such the following have been changed:
|
||||
|
||||
* :meth:`~mongoengine.queryset.QuerySet.sum`
|
||||
* :meth:`~mongoengine.queryset.QuerySet.average`
|
||||
@@ -51,8 +112,8 @@ the following have been changed:
|
||||
Default collection naming
|
||||
-------------------------
|
||||
|
||||
Previously it was just lowercase, its now much more pythonic and readable as its
|
||||
lowercase and underscores, previously ::
|
||||
Previously it was just lowercase, its now much more pythonic and readable as
|
||||
its lowercase and underscores, previously ::
|
||||
|
||||
class MyAceDocument(Document):
|
||||
pass
|
||||
@@ -88,7 +149,8 @@ Alternatively, you can rename your collections eg ::
|
||||
|
||||
failure = False
|
||||
|
||||
collection_names = [d._get_collection_name() for d in _document_registry.values()]
|
||||
collection_names = [d._get_collection_name()
|
||||
for d in _document_registry.values()]
|
||||
|
||||
for new_style_name in collection_names:
|
||||
if not new_style_name: # embedded documents don't have collections
|
||||
@@ -106,7 +168,8 @@ Alternatively, you can rename your collections eg ::
|
||||
old_style_name, new_style_name)
|
||||
else:
|
||||
db[old_style_name].rename(new_style_name)
|
||||
print "Renamed: %s to %s" % (old_style_name, new_style_name)
|
||||
print "Renamed: %s to %s" % (old_style_name,
|
||||
new_style_name)
|
||||
|
||||
if failure:
|
||||
print "Upgrading collection names failed"
|
||||
|
@@ -12,13 +12,12 @@ from signals import *
|
||||
__all__ = (document.__all__ + fields.__all__ + connection.__all__ +
|
||||
queryset.__all__ + signals.__all__)
|
||||
|
||||
VERSION = (0, 6, 20)
|
||||
VERSION = (0, '7rc1')
|
||||
|
||||
|
||||
def get_version():
|
||||
version = '%s.%s' % (VERSION[0], VERSION[1])
|
||||
if VERSION[2]:
|
||||
version = '%s.%s' % (version, VERSION[2])
|
||||
return version
|
||||
if isinstance(VERSION[-1], basestring):
|
||||
return '.'.join(map(str, VERSION[:-1])) + VERSION[-1]
|
||||
return '.'.join(map(str, VERSION))
|
||||
|
||||
__version__ = get_version()
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -39,9 +39,26 @@ class DeReference(object):
|
||||
doc_type = doc_type.field
|
||||
|
||||
if isinstance(doc_type, ReferenceField):
|
||||
field = doc_type
|
||||
doc_type = doc_type.document_type
|
||||
if all([i.__class__ == doc_type for i in 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()]):
|
||||
return items
|
||||
elif not field.dbref:
|
||||
if not hasattr(items, 'items'):
|
||||
items = [field.to_python(v)
|
||||
if not isinstance(v, (DBRef, Document)) else v
|
||||
for v in items]
|
||||
else:
|
||||
items = dict([
|
||||
(k, field.to_python(v))
|
||||
if not isinstance(v, (DBRef, Document)) else (k, v)
|
||||
for k, v in items.iteritems()]
|
||||
)
|
||||
|
||||
self.reference_map = self._find_references(items)
|
||||
self.object_map = self._fetch_objects(doc_type=doc_type)
|
||||
|
@@ -1,4 +1,3 @@
|
||||
from django.http import Http404
|
||||
from mongoengine.queryset import QuerySet
|
||||
from mongoengine.base import BaseDocument
|
||||
from mongoengine.base import ValidationError
|
||||
@@ -27,6 +26,7 @@ def get_document_or_404(cls, *args, **kwargs):
|
||||
try:
|
||||
return queryset.get(*args, **kwargs)
|
||||
except (queryset._document.DoesNotExist, ValidationError):
|
||||
from django.http import Http404
|
||||
raise Http404('No %s matches the given query.' % queryset._document._class_name)
|
||||
|
||||
def get_list_or_404(cls, *args, **kwargs):
|
||||
@@ -42,5 +42,6 @@ def get_list_or_404(cls, *args, **kwargs):
|
||||
queryset = _get_queryset(cls)
|
||||
obj_list = list(queryset.filter(*args, **kwargs))
|
||||
if not obj_list:
|
||||
from django.http import Http404
|
||||
raise Http404('No %s matches the given query.' % queryset._document._class_name)
|
||||
return obj_list
|
||||
|
@@ -1,10 +1,28 @@
|
||||
#coding: utf-8
|
||||
from django.test import TestCase
|
||||
from django.conf import settings
|
||||
from nose.plugins.skip import SkipTest
|
||||
|
||||
from mongoengine.python_support import PY3
|
||||
from mongoengine import connect
|
||||
|
||||
try:
|
||||
from django.test import TestCase
|
||||
from django.conf import settings
|
||||
except Exception as err:
|
||||
if PY3:
|
||||
from unittest import TestCase
|
||||
# Dummy value so no error
|
||||
class settings:
|
||||
MONGO_DATABASE_NAME = 'dummy'
|
||||
else:
|
||||
raise err
|
||||
|
||||
|
||||
class MongoTestCase(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
if PY3:
|
||||
raise SkipTest('django does not have Python 3 support')
|
||||
|
||||
"""
|
||||
TestCase class that clear the collection between the tests
|
||||
"""
|
||||
|
@@ -1,15 +1,19 @@
|
||||
import warnings
|
||||
|
||||
import pymongo
|
||||
import re
|
||||
|
||||
from bson.dbref import DBRef
|
||||
from mongoengine import signals, queryset
|
||||
|
||||
from mongoengine import signals
|
||||
from base import (DocumentMetaclass, TopLevelDocumentMetaclass, BaseDocument,
|
||||
BaseDict, BaseList)
|
||||
from queryset import OperationError
|
||||
from queryset import OperationError, NotUniqueError
|
||||
from connection import get_db, DEFAULT_CONNECTION_NAME
|
||||
|
||||
__all__ = ['Document', 'EmbeddedDocument', 'DynamicDocument',
|
||||
'DynamicEmbeddedDocument', 'OperationError', 'InvalidCollectionError']
|
||||
'DynamicEmbeddedDocument', 'OperationError',
|
||||
'InvalidCollectionError', 'NotUniqueError']
|
||||
|
||||
|
||||
class InvalidCollectionError(Exception):
|
||||
@@ -23,6 +27,9 @@ class EmbeddedDocument(BaseDocument):
|
||||
:class:`~mongoengine.EmbeddedDocumentField` field type.
|
||||
"""
|
||||
|
||||
# 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
|
||||
my_metaclass = DocumentMetaclass
|
||||
__metaclass__ = DocumentMetaclass
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -91,9 +98,12 @@ class Document(BaseDocument):
|
||||
disabled by either setting types to False on the specific index or
|
||||
by setting index_types to False on the meta dictionary for the document.
|
||||
"""
|
||||
|
||||
# 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
|
||||
my_metaclass = TopLevelDocumentMetaclass
|
||||
__metaclass__ = TopLevelDocumentMetaclass
|
||||
|
||||
@apply
|
||||
def pk():
|
||||
"""Primary key alias
|
||||
"""
|
||||
@@ -102,6 +112,7 @@ class Document(BaseDocument):
|
||||
def fset(self, value):
|
||||
return setattr(self, self._meta['id_field'], value)
|
||||
return property(fget, fset)
|
||||
pk = pk()
|
||||
|
||||
@classmethod
|
||||
def _get_db(cls):
|
||||
@@ -127,8 +138,9 @@ class Document(BaseDocument):
|
||||
options = cls._collection.options()
|
||||
if options.get('max') != max_documents or \
|
||||
options.get('size') != max_size:
|
||||
msg = ('Cannot create collection "%s" as a capped '
|
||||
'collection as it already exists') % cls._collection
|
||||
msg = (('Cannot create collection "%s" as a capped '
|
||||
'collection as it already exists')
|
||||
% cls._collection)
|
||||
raise InvalidCollectionError(msg)
|
||||
else:
|
||||
# Create the collection as a capped collection
|
||||
@@ -142,8 +154,9 @@ class Document(BaseDocument):
|
||||
cls._collection = db[collection_name]
|
||||
return cls._collection
|
||||
|
||||
def save(self, safe=True, force_insert=False, validate=True, write_options=None,
|
||||
cascade=None, cascade_kwargs=None, _refs=None):
|
||||
def save(self, safe=True, force_insert=False, validate=True,
|
||||
write_options=None, cascade=None, cascade_kwargs=None,
|
||||
_refs=None):
|
||||
"""Save the :class:`~mongoengine.Document` to the database. If the
|
||||
document already exists, it will be updated, otherwise it will be
|
||||
created.
|
||||
@@ -156,27 +169,30 @@ class Document(BaseDocument):
|
||||
updates of existing documents
|
||||
:param validate: validates the document; set to ``False`` to skip.
|
||||
:param write_options: Extra keyword arguments are passed down to
|
||||
:meth:`~pymongo.collection.Collection.save` OR
|
||||
:meth:`~pymongo.collection.Collection.insert`
|
||||
which will be used as options for the resultant ``getLastError`` command.
|
||||
For example, ``save(..., write_options={w: 2, fsync: True}, ...)`` will
|
||||
wait until at least two servers have recorded the write and will force an
|
||||
fsync on each server being written to.
|
||||
:param cascade: Sets the flag for cascading saves. You can set a default by setting
|
||||
"cascade" in the document __meta__
|
||||
:param cascade_kwargs: optional kwargs dictionary to be passed throw to cascading saves
|
||||
:meth:`~pymongo.collection.Collection.save` OR
|
||||
:meth:`~pymongo.collection.Collection.insert`
|
||||
which will be used as options for the resultant
|
||||
``getLastError`` command. For example,
|
||||
``save(..., write_options={w: 2, fsync: True}, ...)`` will
|
||||
wait until at least two servers have recorded the write and
|
||||
will force an fsync on the primary server.
|
||||
:param cascade: Sets the flag for cascading saves. You can set a
|
||||
default by setting "cascade" in the document __meta__
|
||||
:param cascade_kwargs: optional kwargs dictionary to be passed throw
|
||||
to cascading saves
|
||||
:param _refs: A list of processed references used in cascading saves
|
||||
|
||||
.. versionchanged:: 0.5
|
||||
In existing documents it only saves changed fields using set / unset
|
||||
Saves are cascaded and any :class:`~bson.dbref.DBRef` objects
|
||||
that have changes are saved as well.
|
||||
In existing documents it only saves changed fields using
|
||||
set / unset. Saves are cascaded and any
|
||||
:class:`~bson.dbref.DBRef` objects that have changes are
|
||||
saved as well.
|
||||
.. versionchanged:: 0.6
|
||||
Cascade saves are optional = defaults to True, if you want fine grain
|
||||
control then you can turn off using document meta['cascade'] = False
|
||||
Also you can pass different kwargs to the cascade save using cascade_kwargs
|
||||
which overwrites the existing kwargs with custom values
|
||||
|
||||
Cascade saves are optional = defaults to True, if you want
|
||||
fine grain control then you can turn off using document
|
||||
meta['cascade'] = False Also you can pass different kwargs to
|
||||
the cascade save using cascade_kwargs which overwrites the
|
||||
existing kwargs with custom values
|
||||
"""
|
||||
signals.pre_save.send(self.__class__, document=self)
|
||||
|
||||
@@ -194,13 +210,14 @@ class Document(BaseDocument):
|
||||
collection = self.__class__.objects._collection
|
||||
if created:
|
||||
if force_insert:
|
||||
object_id = collection.insert(doc, safe=safe, **write_options)
|
||||
object_id = collection.insert(doc, safe=safe,
|
||||
**write_options)
|
||||
else:
|
||||
object_id = collection.save(doc, safe=safe, **write_options)
|
||||
object_id = collection.save(doc, safe=safe,
|
||||
**write_options)
|
||||
else:
|
||||
object_id = doc['_id']
|
||||
updates, removals = self._delta()
|
||||
|
||||
# Need to add shard key to query, or you get an error
|
||||
select_dict = {'_id': object_id}
|
||||
shard_key = self.__class__._meta.get('shard_key', tuple())
|
||||
@@ -210,11 +227,15 @@ class Document(BaseDocument):
|
||||
|
||||
upsert = self._created
|
||||
if updates:
|
||||
collection.update(select_dict, {"$set": updates}, upsert=upsert, safe=safe, **write_options)
|
||||
collection.update(select_dict, {"$set": updates},
|
||||
upsert=upsert, safe=safe, **write_options)
|
||||
if removals:
|
||||
collection.update(select_dict, {"$unset": removals}, upsert=upsert, safe=safe, **write_options)
|
||||
collection.update(select_dict, {"$unset": removals},
|
||||
upsert=upsert, safe=safe, **write_options)
|
||||
|
||||
cascade = self._meta.get('cascade', True) if cascade is None else cascade
|
||||
warn_cascade = not cascade and 'cascade' not in self._meta
|
||||
cascade = (self._meta.get('cascade', True)
|
||||
if cascade is None else cascade)
|
||||
if cascade:
|
||||
kwargs = {
|
||||
"safe": safe,
|
||||
@@ -226,40 +247,49 @@ class Document(BaseDocument):
|
||||
if cascade_kwargs: # Allow granular control over cascades
|
||||
kwargs.update(cascade_kwargs)
|
||||
kwargs['_refs'] = _refs
|
||||
#self._changed_fields = []
|
||||
self.cascade_save(**kwargs)
|
||||
self.cascade_save(warn_cascade=warn_cascade, **kwargs)
|
||||
|
||||
except pymongo.errors.OperationFailure, err:
|
||||
message = 'Could not save document (%s)'
|
||||
if u'duplicate key' in unicode(err):
|
||||
if re.match('^E1100[01] duplicate key', unicode(err)):
|
||||
# E11000 - duplicate key error index
|
||||
# E11001 - duplicate key on update
|
||||
message = u'Tried to save duplicate unique keys (%s)'
|
||||
raise NotUniqueError(message % unicode(err))
|
||||
raise OperationError(message % unicode(err))
|
||||
id_field = self._meta['id_field']
|
||||
self[id_field] = self._fields[id_field].to_python(object_id)
|
||||
if id_field not in self._meta.get('shard_key', []):
|
||||
self[id_field] = self._fields[id_field].to_python(object_id)
|
||||
|
||||
self._changed_fields = []
|
||||
self._created = False
|
||||
signals.post_save.send(self.__class__, document=self, created=created)
|
||||
return self
|
||||
|
||||
def cascade_save(self, *args, **kwargs):
|
||||
"""Recursively saves any references / generic references on an object"""
|
||||
from fields import ReferenceField, GenericReferenceField
|
||||
def cascade_save(self, warn_cascade=None, *args, **kwargs):
|
||||
"""Recursively saves any references /
|
||||
generic references on an objects"""
|
||||
import fields
|
||||
_refs = kwargs.get('_refs', []) or []
|
||||
|
||||
for name, cls in self._fields.items():
|
||||
|
||||
if not isinstance(cls, (ReferenceField, GenericReferenceField)):
|
||||
if not isinstance(cls, (fields.ReferenceField,
|
||||
fields.GenericReferenceField)):
|
||||
continue
|
||||
|
||||
ref = getattr(self, name)
|
||||
if not ref:
|
||||
if not ref or isinstance(ref, DBRef):
|
||||
continue
|
||||
if isinstance(ref, DBRef):
|
||||
|
||||
if not getattr(ref, '_changed_fields', True):
|
||||
continue
|
||||
|
||||
ref_id = "%s,%s" % (ref.__class__.__name__, str(ref._data))
|
||||
if ref and ref_id not in _refs:
|
||||
if warn_cascade:
|
||||
msg = ("Cascading saves will default to off in 0.8, "
|
||||
"please explicitly set `.save(cascade=True)`")
|
||||
warnings.warn(msg, FutureWarning)
|
||||
_refs.append(ref_id)
|
||||
kwargs["_refs"] = _refs
|
||||
ref.save(**kwargs)
|
||||
@@ -304,8 +334,8 @@ class Document(BaseDocument):
|
||||
|
||||
.. versionadded:: 0.5
|
||||
"""
|
||||
from dereference import DeReference
|
||||
self._data = DeReference()(self._data, max_depth)
|
||||
import dereference
|
||||
self._data = dereference.DeReference()(self._data, max_depth)
|
||||
return self
|
||||
|
||||
def reload(self, max_depth=1):
|
||||
@@ -353,17 +383,18 @@ class Document(BaseDocument):
|
||||
"""This method registers the delete rules to apply when removing this
|
||||
object.
|
||||
"""
|
||||
cls._meta['delete_rules'][(document_cls, field_name)] = rule
|
||||
delete_rules = cls._meta.get('delete_rules') or {}
|
||||
delete_rules[(document_cls, field_name)] = rule
|
||||
cls._meta['delete_rules'] = delete_rules
|
||||
|
||||
@classmethod
|
||||
def drop_collection(cls):
|
||||
"""Drops the entire collection associated with this
|
||||
:class:`~mongoengine.Document` type from the database.
|
||||
"""
|
||||
from mongoengine.queryset import QuerySet
|
||||
db = cls._get_db()
|
||||
db.drop_collection(cls._get_collection_name())
|
||||
QuerySet._reset_already_indexed(cls)
|
||||
queryset.QuerySet._reset_already_indexed(cls)
|
||||
|
||||
|
||||
class DynamicDocument(Document):
|
||||
@@ -379,7 +410,12 @@ class DynamicDocument(Document):
|
||||
|
||||
There is one caveat on Dynamic Documents: fields cannot start with `_`
|
||||
"""
|
||||
|
||||
# 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
|
||||
my_metaclass = TopLevelDocumentMetaclass
|
||||
__metaclass__ = TopLevelDocumentMetaclass
|
||||
|
||||
_dynamic = True
|
||||
|
||||
def __delattr__(self, *args, **kwargs):
|
||||
@@ -398,7 +434,11 @@ class DynamicEmbeddedDocument(EmbeddedDocument):
|
||||
information about dynamic documents.
|
||||
"""
|
||||
|
||||
# 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
|
||||
my_metaclass = DocumentMetaclass
|
||||
__metaclass__ = DocumentMetaclass
|
||||
|
||||
_dynamic = True
|
||||
|
||||
def __delattr__(self, *args, **kwargs):
|
||||
|
@@ -1,18 +1,22 @@
|
||||
import datetime
|
||||
import time
|
||||
import decimal
|
||||
import gridfs
|
||||
import re
|
||||
import time
|
||||
import uuid
|
||||
import warnings
|
||||
import itertools
|
||||
from operator import itemgetter
|
||||
|
||||
import gridfs
|
||||
from bson import Binary, DBRef, SON, ObjectId
|
||||
|
||||
from mongoengine.python_support import (PY3, bin_type, txt_type,
|
||||
str_types, StringIO)
|
||||
from base import (BaseField, ComplexBaseField, ObjectIdField,
|
||||
ValidationError, get_document, BaseDocument)
|
||||
from queryset import DO_NOTHING, QuerySet
|
||||
from document import Document, EmbeddedDocument
|
||||
from connection import get_db, DEFAULT_CONNECTION_NAME
|
||||
from operator import itemgetter
|
||||
|
||||
|
||||
try:
|
||||
@@ -21,12 +25,6 @@ except ImportError:
|
||||
Image = None
|
||||
ImageOps = None
|
||||
|
||||
try:
|
||||
from cStringIO import StringIO
|
||||
except ImportError:
|
||||
from StringIO import StringIO
|
||||
|
||||
|
||||
__all__ = ['StringField', 'IntField', 'FloatField', 'BooleanField',
|
||||
'DateTimeField', 'EmbeddedDocumentField', 'ListField', 'DictField',
|
||||
'ObjectIdField', 'ReferenceField', 'ValidationError', 'MapField',
|
||||
@@ -51,8 +49,11 @@ class StringField(BaseField):
|
||||
def to_python(self, value):
|
||||
if isinstance(value, unicode):
|
||||
return value
|
||||
else:
|
||||
return value.decode('utf-8')
|
||||
try:
|
||||
value = value.decode('utf-8')
|
||||
except:
|
||||
pass
|
||||
return value
|
||||
|
||||
def validate(self, value):
|
||||
if not isinstance(value, basestring):
|
||||
@@ -152,7 +153,11 @@ class IntField(BaseField):
|
||||
super(IntField, self).__init__(**kwargs)
|
||||
|
||||
def to_python(self, value):
|
||||
return int(value)
|
||||
try:
|
||||
value = int(value)
|
||||
except ValueError:
|
||||
pass
|
||||
return value
|
||||
|
||||
def validate(self, value):
|
||||
try:
|
||||
@@ -182,7 +187,11 @@ class FloatField(BaseField):
|
||||
super(FloatField, self).__init__(**kwargs)
|
||||
|
||||
def to_python(self, value):
|
||||
return float(value)
|
||||
try:
|
||||
value = float(value)
|
||||
except ValueError:
|
||||
pass
|
||||
return value
|
||||
|
||||
def validate(self, value):
|
||||
if isinstance(value, int):
|
||||
@@ -214,9 +223,14 @@ class DecimalField(BaseField):
|
||||
super(DecimalField, self).__init__(**kwargs)
|
||||
|
||||
def to_python(self, value):
|
||||
original_value = value
|
||||
if not isinstance(value, basestring):
|
||||
value = unicode(value)
|
||||
return decimal.Decimal(value)
|
||||
try:
|
||||
value = decimal.Decimal(value)
|
||||
except ValueError:
|
||||
return original_value
|
||||
return value
|
||||
|
||||
def to_mongo(self, value):
|
||||
return unicode(value)
|
||||
@@ -244,7 +258,11 @@ class BooleanField(BaseField):
|
||||
"""
|
||||
|
||||
def to_python(self, value):
|
||||
return bool(value)
|
||||
try:
|
||||
value = bool(value)
|
||||
except ValueError:
|
||||
pass
|
||||
return value
|
||||
|
||||
def validate(self, value):
|
||||
if not isinstance(value, bool):
|
||||
@@ -375,6 +393,8 @@ class ComplexDateTimeField(StringField):
|
||||
data = super(ComplexDateTimeField, self).__get__(instance, owner)
|
||||
if data == None:
|
||||
return datetime.datetime.now()
|
||||
if isinstance(data, datetime.datetime):
|
||||
return data
|
||||
return self._convert_from_string(data)
|
||||
|
||||
def __set__(self, instance, value):
|
||||
@@ -387,7 +407,11 @@ class ComplexDateTimeField(StringField):
|
||||
'ComplexDateTimeField')
|
||||
|
||||
def to_python(self, value):
|
||||
return self._convert_from_string(value)
|
||||
original_value = value
|
||||
try:
|
||||
return self._convert_from_string(value)
|
||||
except:
|
||||
return original_value
|
||||
|
||||
def to_mongo(self, value):
|
||||
return self._convert_from_datetime(value)
|
||||
@@ -451,8 +475,9 @@ class GenericEmbeddedDocumentField(BaseField):
|
||||
|
||||
Only valid values are subclasses of :class:`~mongoengine.EmbeddedDocument`.
|
||||
|
||||
.. note:: You can use the choices param to limit the acceptable
|
||||
EmbeddedDocument types
|
||||
.. note ::
|
||||
You can use the choices param to limit the acceptable
|
||||
EmbeddedDocument types
|
||||
"""
|
||||
|
||||
def prepare_query_value(self, op, value):
|
||||
@@ -670,7 +695,8 @@ class ReferenceField(BaseField):
|
||||
* NULLIFY - Updates the reference to null.
|
||||
* CASCADE - Deletes the documents associated with the reference.
|
||||
* DENY - Prevent the deletion of the reference object.
|
||||
* PULL - Pull the reference from a :class:`~mongoengine.ListField` of references
|
||||
* PULL - Pull the reference from a :class:`~mongoengine.ListField`
|
||||
of references
|
||||
|
||||
Alternative syntax for registering delete rules (useful when implementing
|
||||
bi-directional delete rules)
|
||||
@@ -686,9 +712,12 @@ class ReferenceField(BaseField):
|
||||
.. versionchanged:: 0.5 added `reverse_delete_rule`
|
||||
"""
|
||||
|
||||
def __init__(self, document_type, reverse_delete_rule=DO_NOTHING, **kwargs):
|
||||
def __init__(self, document_type, dbref=None,
|
||||
reverse_delete_rule=DO_NOTHING, **kwargs):
|
||||
"""Initialises the Reference Field.
|
||||
|
||||
:param dbref: Store the reference as :class:`~pymongo.dbref.DBRef`
|
||||
or as the :class:`~pymongo.objectid.ObjectId`.id .
|
||||
:param reverse_delete_rule: Determines what to do when the referring
|
||||
object is deleted
|
||||
"""
|
||||
@@ -696,6 +725,13 @@ class ReferenceField(BaseField):
|
||||
if not issubclass(document_type, (Document, basestring)):
|
||||
self.error('Argument to ReferenceField constructor must be a '
|
||||
'document class or a string')
|
||||
|
||||
if dbref is None:
|
||||
msg = ("ReferenceFields will default to using ObjectId "
|
||||
" strings in 0.8, set DBRef=True if this isn't desired")
|
||||
warnings.warn(msg, FutureWarning)
|
||||
|
||||
self.dbref = dbref if dbref is not None else True # To change in 0.8
|
||||
self.document_type_obj = document_type
|
||||
self.reverse_delete_rule = reverse_delete_rule
|
||||
super(ReferenceField, self).__init__(**kwargs)
|
||||
@@ -718,8 +754,9 @@ class ReferenceField(BaseField):
|
||||
|
||||
# Get value from document instance if available
|
||||
value = instance._data.get(self.name)
|
||||
|
||||
# Dereference DBRefs
|
||||
if isinstance(value, (DBRef)):
|
||||
if isinstance(value, DBRef):
|
||||
value = self.document_type._get_db().dereference(value)
|
||||
if value is not None:
|
||||
instance._data[self.name] = self.document_type._from_son(value)
|
||||
@@ -728,6 +765,10 @@ class ReferenceField(BaseField):
|
||||
|
||||
def to_mongo(self, document):
|
||||
if isinstance(document, DBRef):
|
||||
if not self.dbref:
|
||||
return "%s" % DBRef.id
|
||||
return document
|
||||
elif not self.dbref and isinstance(document, basestring):
|
||||
return document
|
||||
|
||||
id_field_name = self.document_type._meta['id_field']
|
||||
@@ -735,7 +776,7 @@ class ReferenceField(BaseField):
|
||||
|
||||
if isinstance(document, Document):
|
||||
# We need the id from the saved object to create the DBRef
|
||||
id_ = document.id
|
||||
id_ = document.pk
|
||||
if id_ is None:
|
||||
self.error('You can only reference documents once they have'
|
||||
' been saved to the database')
|
||||
@@ -743,18 +784,30 @@ class ReferenceField(BaseField):
|
||||
id_ = document
|
||||
|
||||
id_ = id_field.to_mongo(id_)
|
||||
collection = self.document_type._get_collection_name()
|
||||
return DBRef(collection, id_)
|
||||
if self.dbref:
|
||||
collection = self.document_type._get_collection_name()
|
||||
return DBRef(collection, id_)
|
||||
|
||||
return "%s" % id_
|
||||
|
||||
def to_python(self, value):
|
||||
"""Convert a MongoDB-compatible type to a Python type.
|
||||
"""
|
||||
if (not self.dbref and
|
||||
not isinstance(value, (DBRef, Document, EmbeddedDocument))):
|
||||
collection = self.document_type._get_collection_name()
|
||||
value = DBRef(collection, self.document_type.id.to_python(value))
|
||||
return value
|
||||
|
||||
def prepare_query_value(self, op, value):
|
||||
if value is None:
|
||||
return None
|
||||
|
||||
return self.to_mongo(value)
|
||||
|
||||
def validate(self, value):
|
||||
|
||||
if not isinstance(value, (self.document_type, DBRef)):
|
||||
self.error('A ReferenceField only accepts DBRef')
|
||||
self.error("A ReferenceField only accepts DBRef or documents")
|
||||
|
||||
if isinstance(value, Document) and value.id is None:
|
||||
self.error('You can only reference documents once they have been '
|
||||
@@ -768,10 +821,12 @@ class GenericReferenceField(BaseField):
|
||||
"""A reference to *any* :class:`~mongoengine.document.Document` subclass
|
||||
that will be automatically dereferenced on access (lazily).
|
||||
|
||||
.. note:: Any documents used as a generic reference must be registered in the
|
||||
document registry. Importing the model will automatically register it.
|
||||
.. note ::
|
||||
* Any documents used as a generic reference must be registered in the
|
||||
document registry. Importing the model will automatically register
|
||||
it.
|
||||
|
||||
.. note:: You can use the choices param to limit the acceptable Document types
|
||||
* You can use the choices param to limit the acceptable Document types
|
||||
|
||||
.. versionadded:: 0.3
|
||||
"""
|
||||
@@ -842,12 +897,20 @@ class BinaryField(BaseField):
|
||||
self.max_bytes = max_bytes
|
||||
super(BinaryField, self).__init__(**kwargs)
|
||||
|
||||
def __set__(self, instance, value):
|
||||
"""Handle bytearrays in python 3.1"""
|
||||
if PY3 and isinstance(value, bytearray):
|
||||
value = bin_type(value)
|
||||
return super(BinaryField, self).__set__(instance, value)
|
||||
|
||||
def to_mongo(self, value):
|
||||
return Binary(value)
|
||||
|
||||
def validate(self, value):
|
||||
if not isinstance(value, (basestring, Binary)):
|
||||
self.error('BinaryField only accepts string or bson Binary values')
|
||||
if not isinstance(value, (bin_type, txt_type, Binary)):
|
||||
self.error("BinaryField only accepts instances of "
|
||||
"(%s, %s, Binary)" % (
|
||||
bin_type.__name__, txt_type.__name__))
|
||||
|
||||
if self.max_bytes is not None and len(value) > self.max_bytes:
|
||||
self.error('Binary value is too long')
|
||||
@@ -903,11 +966,13 @@ class GridFSProxy(object):
|
||||
def __repr__(self):
|
||||
return '<%s: %s>' % (self.__class__.__name__, self.grid_id)
|
||||
|
||||
def __cmp__(self, other):
|
||||
if not isinstance(other, GridFSProxy):
|
||||
return -1
|
||||
return cmp((self.grid_id, self.collection_name, self.db_alias),
|
||||
(other.grid_id, other.collection_name, other.db_alias))
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, GridFSProxy):
|
||||
return ((self.grid_id == other.grid_id) and
|
||||
(self.collection_name == other.collection_name) and
|
||||
(self.db_alias == other.db_alias))
|
||||
else:
|
||||
return False
|
||||
|
||||
@property
|
||||
def fs(self):
|
||||
@@ -1020,7 +1085,8 @@ class FileField(BaseField):
|
||||
|
||||
def __set__(self, instance, value):
|
||||
key = self.name
|
||||
if (hasattr(value, 'read') and not isinstance(value, GridFSProxy)) or isinstance(value, basestring):
|
||||
if ((hasattr(value, 'read') and not
|
||||
isinstance(value, GridFSProxy)) or isinstance(value, str_types)):
|
||||
# using "FileField() = file/string" notation
|
||||
grid_file = instance._data.get(self.name)
|
||||
# If a file already exists, delete it
|
||||
@@ -1076,6 +1142,7 @@ class ImageGridFsProxy(GridFSProxy):
|
||||
|
||||
try:
|
||||
img = Image.open(file_obj)
|
||||
img_format = img.format
|
||||
except:
|
||||
raise ValidationError('Invalid image')
|
||||
|
||||
@@ -1110,20 +1177,20 @@ class ImageGridFsProxy(GridFSProxy):
|
||||
|
||||
if thumbnail:
|
||||
thumb_id = self._put_thumbnail(thumbnail,
|
||||
img.format)
|
||||
img_format)
|
||||
else:
|
||||
thumb_id = None
|
||||
|
||||
w, h = img.size
|
||||
|
||||
io = StringIO()
|
||||
img.save(io, img.format)
|
||||
img.save(io, img_format)
|
||||
io.seek(0)
|
||||
|
||||
return super(ImageGridFsProxy, self).put(io,
|
||||
width=w,
|
||||
height=h,
|
||||
format=img.format,
|
||||
format=img_format,
|
||||
thumbnail_id=thumb_id,
|
||||
**kwargs)
|
||||
|
||||
@@ -1209,11 +1276,15 @@ class ImageField(FileField):
|
||||
params_size = ('width', 'height', 'force')
|
||||
extra_args = dict(size=size, thumbnail_size=thumbnail_size)
|
||||
for att_name, att in extra_args.items():
|
||||
if att and (isinstance(att, tuple) or isinstance(att, list)):
|
||||
setattr(self, att_name, dict(
|
||||
map(None, params_size, att)))
|
||||
else:
|
||||
setattr(self, att_name, None)
|
||||
value = None
|
||||
if isinstance(att, (tuple, list)):
|
||||
if PY3:
|
||||
value = dict(itertools.zip_longest(params_size, att,
|
||||
fillvalue=None))
|
||||
else:
|
||||
value = dict(map(None, params_size, att))
|
||||
|
||||
setattr(self, att_name, value)
|
||||
|
||||
super(ImageField, self).__init__(
|
||||
collection_name=collection_name,
|
||||
@@ -1255,18 +1326,19 @@ class SequenceField(IntField):
|
||||
|
||||
.. versionadded:: 0.5
|
||||
"""
|
||||
def __init__(self, collection_name=None, db_alias = None, *args, **kwargs):
|
||||
def __init__(self, collection_name=None, db_alias = None, sequence_name = None, *args, **kwargs):
|
||||
self.collection_name = collection_name or 'mongoengine.counters'
|
||||
self.db_alias = db_alias or DEFAULT_CONNECTION_NAME
|
||||
self.sequence_name = sequence_name
|
||||
return super(SequenceField, self).__init__(*args, **kwargs)
|
||||
|
||||
def generate_new_value(self):
|
||||
"""
|
||||
Generate and Increment the counter
|
||||
"""
|
||||
sequence_id = "{0}.{1}".format(self.owner_document._get_collection_name(),
|
||||
self.name)
|
||||
collection = get_db(alias = self.db_alias )[self.collection_name]
|
||||
sequence_name = self.sequence_name or self.owner_document._get_collection_name()
|
||||
sequence_id = "%s.%s" % (sequence_name, self.name)
|
||||
collection = get_db(alias=self.db_alias)[self.collection_name]
|
||||
counter = collection.find_and_modify(query={"_id": sequence_id},
|
||||
update={"$inc": {"next": 1}},
|
||||
new=True,
|
||||
@@ -1328,9 +1400,13 @@ class UUIDField(BaseField):
|
||||
|
||||
def to_python(self, value):
|
||||
if not self._binary:
|
||||
if not isinstance(value, basestring):
|
||||
value = unicode(value)
|
||||
return uuid.UUID(value)
|
||||
original_value = value
|
||||
try:
|
||||
if not isinstance(value, basestring):
|
||||
value = unicode(value)
|
||||
return uuid.UUID(value)
|
||||
except:
|
||||
return original_value
|
||||
return value
|
||||
|
||||
def to_mongo(self, value):
|
||||
|
60
mongoengine/python_support.py
Normal file
60
mongoengine/python_support.py
Normal file
@@ -0,0 +1,60 @@
|
||||
"""Helper functions and types to aid with Python 2.5 - 3 support."""
|
||||
|
||||
import sys
|
||||
|
||||
PY3 = sys.version_info[0] == 3
|
||||
PY25 = sys.version_info[:2] == (2, 5)
|
||||
|
||||
if PY3:
|
||||
import codecs
|
||||
from io import BytesIO as StringIO
|
||||
# return s converted to binary. b('test') should be equivalent to b'test'
|
||||
def b(s):
|
||||
return codecs.latin_1_encode(s)[0]
|
||||
|
||||
bin_type = bytes
|
||||
txt_type = str
|
||||
else:
|
||||
try:
|
||||
from cStringIO import StringIO
|
||||
except ImportError:
|
||||
from StringIO import StringIO
|
||||
|
||||
# Conversion to binary only necessary in Python 3
|
||||
def b(s):
|
||||
return s
|
||||
|
||||
bin_type = str
|
||||
txt_type = unicode
|
||||
|
||||
str_types = (bin_type, txt_type)
|
||||
|
||||
if PY25:
|
||||
def product(*args, **kwds):
|
||||
pools = map(tuple, args) * kwds.get('repeat', 1)
|
||||
result = [[]]
|
||||
for pool in pools:
|
||||
result = [x + [y] for x in result for y in pool]
|
||||
for prod in result:
|
||||
yield tuple(prod)
|
||||
reduce = reduce
|
||||
else:
|
||||
from itertools import product
|
||||
from functools import reduce
|
||||
|
||||
|
||||
# For use with Python 2.5
|
||||
# converts all keys from unicode to str for d and all nested dictionaries
|
||||
def to_str_keys_recursive(d):
|
||||
if isinstance(d, list):
|
||||
for val in d:
|
||||
if isinstance(val, (dict, list)):
|
||||
to_str_keys_recursive(val)
|
||||
elif isinstance(d, dict):
|
||||
for key, val in d.items():
|
||||
if isinstance(val, (dict, list)):
|
||||
to_str_keys_recursive(val)
|
||||
if isinstance(key, unicode):
|
||||
d[str(key)] = d.pop(key)
|
||||
else:
|
||||
raise ValueError("non list/dict parameter not allowed")
|
@@ -4,8 +4,11 @@ import copy
|
||||
import itertools
|
||||
import operator
|
||||
|
||||
from collections import defaultdict
|
||||
from functools import partial
|
||||
|
||||
from mongoengine.python_support import product, reduce
|
||||
|
||||
import pymongo
|
||||
from bson.code import Code
|
||||
|
||||
@@ -42,6 +45,10 @@ class OperationError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class NotUniqueError(OperationError):
|
||||
pass
|
||||
|
||||
|
||||
RE_TYPE = type(re.compile(''))
|
||||
|
||||
|
||||
@@ -120,7 +127,7 @@ class QueryTreeTransformerVisitor(QNodeVisitor):
|
||||
# the necessary parts. Then for each $or part, create a new query
|
||||
# that ANDs the necessary part with the $or part.
|
||||
clauses = []
|
||||
for or_group in itertools.product(*or_groups):
|
||||
for or_group in product(*or_groups):
|
||||
q_object = reduce(lambda a, b: a & b, and_parts, Q())
|
||||
q_object = reduce(lambda a, b: a & b, or_group, q_object)
|
||||
clauses.append(q_object)
|
||||
@@ -329,6 +336,7 @@ class QuerySet(object):
|
||||
"""
|
||||
|
||||
__already_indexed = set()
|
||||
__dereference = False
|
||||
|
||||
def __init__(self, document, collection):
|
||||
self._document = document
|
||||
@@ -348,7 +356,7 @@ class QuerySet(object):
|
||||
|
||||
# If inheritance is allowed, only return instances and instances of
|
||||
# subclasses of the class being used
|
||||
if document._meta.get('allow_inheritance'):
|
||||
if document._meta.get('allow_inheritance') != False:
|
||||
self._initial_query = {'_types': self._document._class_name}
|
||||
self._loaded_fields = QueryFieldList(always_include=['_cls'])
|
||||
self._cursor_obj = None
|
||||
@@ -438,7 +446,7 @@ class QuerySet(object):
|
||||
"""
|
||||
background = self._document._meta.get('index_background', False)
|
||||
drop_dups = self._document._meta.get('index_drop_dups', False)
|
||||
index_opts = self._document._meta.get('index_opts', {})
|
||||
index_opts = self._document._meta.get('index_opts') or {}
|
||||
index_types = self._document._meta.get('index_types', True)
|
||||
|
||||
# determine if an index which we are creating includes
|
||||
@@ -446,6 +454,7 @@ class QuerySet(object):
|
||||
# an extra index on _type, as mongodb will use the existing
|
||||
# index to service queries against _type
|
||||
types_indexed = False
|
||||
|
||||
def includes_types(fields):
|
||||
first_field = None
|
||||
if len(fields):
|
||||
@@ -462,8 +471,8 @@ class QuerySet(object):
|
||||
background=background, drop_dups=drop_dups, **index_opts)
|
||||
|
||||
# Ensure document-defined indexes are created
|
||||
if self._document._meta['indexes']:
|
||||
for spec in self._document._meta['indexes']:
|
||||
if self._document._meta['index_specs']:
|
||||
for spec in self._document._meta['index_specs']:
|
||||
types_indexed = types_indexed or includes_types(spec['fields'])
|
||||
opts = index_opts.copy()
|
||||
opts['unique'] = spec.get('unique', False)
|
||||
@@ -494,7 +503,12 @@ class QuerySet(object):
|
||||
|
||||
index_list = []
|
||||
direction = None
|
||||
use_types = doc_cls._meta.get('allow_inheritance', True)
|
||||
|
||||
allow_inheritance = doc_cls._meta.get('allow_inheritance') != False
|
||||
|
||||
# If sparse - dont include types
|
||||
use_types = allow_inheritance and not spec.get('sparse', False)
|
||||
|
||||
for key in spec['fields']:
|
||||
# Get ASCENDING direction from +, DESCENDING from -, and GEO2D from *
|
||||
direction = pymongo.ASCENDING
|
||||
@@ -510,24 +524,23 @@ class QuerySet(object):
|
||||
parts = key.split('.')
|
||||
if parts in (['pk'], ['id'], ['_id']):
|
||||
key = '_id'
|
||||
fields = []
|
||||
else:
|
||||
fields = QuerySet._lookup_field(doc_cls, parts)
|
||||
parts = [field if field == '_id' else field.db_field for field in fields]
|
||||
parts = [field if field == '_id' else field.db_field
|
||||
for field in fields]
|
||||
key = '.'.join(parts)
|
||||
index_list.append((key, direction))
|
||||
|
||||
# If sparse - dont include types
|
||||
if spec.get('sparse', False):
|
||||
use_types = False
|
||||
|
||||
# Check if a list field is being used, don't use _types if it is
|
||||
if use_types and not all(f._index_with_types for f in fields):
|
||||
use_types = False
|
||||
|
||||
# If _types is being used, prepend it to every specified index
|
||||
index_types = doc_cls._meta.get('index_types', True)
|
||||
allow_inheritance = doc_cls._meta.get('allow_inheritance')
|
||||
if spec.get('types', index_types) and allow_inheritance and use_types and direction is not pymongo.GEO2D:
|
||||
|
||||
if (spec.get('types', index_types) and use_types
|
||||
and direction is not pymongo.GEO2D):
|
||||
index_list.insert(0, ('_types', 1))
|
||||
|
||||
spec['fields'] = index_list
|
||||
@@ -600,7 +613,6 @@ class QuerySet(object):
|
||||
|
||||
if self._hint != -1:
|
||||
self._cursor_obj.hint(self._hint)
|
||||
|
||||
return self._cursor_obj
|
||||
|
||||
@classmethod
|
||||
@@ -678,6 +690,7 @@ class QuerySet(object):
|
||||
custom_operators = ['match']
|
||||
|
||||
mongo_query = {}
|
||||
merge_query = defaultdict(list)
|
||||
for key, value in query.items():
|
||||
if key == "__raw__":
|
||||
mongo_query.update(value)
|
||||
@@ -766,21 +779,22 @@ class QuerySet(object):
|
||||
if op is None or key not in mongo_query:
|
||||
mongo_query[key] = value
|
||||
elif key in mongo_query:
|
||||
if isinstance(mongo_query[key], dict) and isinstance(value, dict):
|
||||
if key in mongo_query and isinstance(mongo_query[key], dict):
|
||||
mongo_query[key].update(value)
|
||||
elif isinstance(mongo_query[key], list):
|
||||
mongo_query[key].append(value)
|
||||
else:
|
||||
mongo_query[key] = [mongo_query[key], value]
|
||||
# Store for manually merging later
|
||||
merge_query[key].append(value)
|
||||
|
||||
for k, v in mongo_query.items():
|
||||
# The queryset has been filter in such a way we must manually merge
|
||||
for k, v in merge_query.items():
|
||||
merge_query[k].append(mongo_query[k])
|
||||
del mongo_query[k]
|
||||
if isinstance(v, list):
|
||||
value = [{k:val} for val in v]
|
||||
if '$and' in mongo_query.keys():
|
||||
mongo_query['$and'].append(value)
|
||||
else:
|
||||
mongo_query['$and'] = value
|
||||
del mongo_query[k]
|
||||
|
||||
return mongo_query
|
||||
|
||||
@@ -914,8 +928,11 @@ class QuerySet(object):
|
||||
ids = self._collection.insert(raw, **write_options)
|
||||
except pymongo.errors.OperationFailure, err:
|
||||
message = 'Could not save document (%s)'
|
||||
if u'duplicate key' in unicode(err):
|
||||
if re.match('^E1100[01] duplicate key', unicode(err)):
|
||||
# E11000 - duplicate key error index
|
||||
# E11001 - duplicate key on update
|
||||
message = u'Tried to save duplicate unique keys (%s)'
|
||||
raise NotUniqueError(message % unicode(err))
|
||||
raise OperationError(message % unicode(err))
|
||||
|
||||
if not load_bulk:
|
||||
@@ -1015,6 +1032,8 @@ class QuerySet(object):
|
||||
:class:`~bson.code.Code` or string
|
||||
:param output: output collection name, if set to 'inline' will try to
|
||||
use :class:`~pymongo.collection.Collection.inline_map_reduce`
|
||||
This can also be a dictionary containing output options
|
||||
see: http://docs.mongodb.org/manual/reference/commands/#mapReduce
|
||||
:param finalize_f: finalize function, an optional function that
|
||||
performs any post-reduction processing.
|
||||
:param scope: values to insert into map/reduce global scope. Optional.
|
||||
@@ -1166,10 +1185,10 @@ class QuerySet(object):
|
||||
|
||||
.. versionadded:: 0.4
|
||||
.. versionchanged:: 0.5 - Fixed handling references
|
||||
.. versionchanged:: 0.6 - Improved db_field refrence handling
|
||||
"""
|
||||
from dereference import DeReference
|
||||
return DeReference()(self._cursor.distinct(field), 1,
|
||||
name=field, instance=self._document)
|
||||
return self._dereference(self._cursor.distinct(field), 1,
|
||||
name=field, instance=self._document)
|
||||
|
||||
def only(self, *fields):
|
||||
"""Load only a subset of this document's fields. ::
|
||||
@@ -1324,9 +1343,10 @@ class QuerySet(object):
|
||||
"""
|
||||
doc = self._document
|
||||
|
||||
delete_rules = doc._meta.get('delete_rules') or {}
|
||||
# Check for DENY rules before actually deleting/nullifying any other
|
||||
# references
|
||||
for rule_entry in doc._meta['delete_rules']:
|
||||
for rule_entry in delete_rules:
|
||||
document_cls, field_name = rule_entry
|
||||
rule = doc._meta['delete_rules'][rule_entry]
|
||||
if rule == DENY and document_cls.objects(**{field_name + '__in': self}).count() > 0:
|
||||
@@ -1334,12 +1354,14 @@ class QuerySet(object):
|
||||
(document_cls.__name__, field_name)
|
||||
raise OperationError(msg)
|
||||
|
||||
for rule_entry in doc._meta['delete_rules']:
|
||||
for rule_entry in delete_rules:
|
||||
document_cls, field_name = rule_entry
|
||||
rule = doc._meta['delete_rules'][rule_entry]
|
||||
if rule == CASCADE:
|
||||
ref_q = document_cls.objects(**{field_name + '__in': self})
|
||||
if doc != document_cls or (doc == document_cls and ref_q.count() > 0):
|
||||
ref_q_count = ref_q.count()
|
||||
if (doc != document_cls and ref_q_count > 0
|
||||
or (doc == document_cls and ref_q_count > 0)):
|
||||
ref_q.delete(safe=safe)
|
||||
elif rule == NULLIFY:
|
||||
document_cls.objects(**{field_name + '__in': self}).update(
|
||||
@@ -1869,10 +1891,16 @@ class QuerySet(object):
|
||||
|
||||
.. versionadded:: 0.5
|
||||
"""
|
||||
from dereference import DeReference
|
||||
# Make select related work the same for querysets
|
||||
max_depth += 1
|
||||
return DeReference()(self, max_depth=max_depth)
|
||||
return self._dereference(self, max_depth=max_depth)
|
||||
|
||||
@property
|
||||
def _dereference(self):
|
||||
if not self.__dereference:
|
||||
from dereference import DeReference
|
||||
self.__dereference = DeReference() # Cached
|
||||
return self.__dereference
|
||||
|
||||
|
||||
class QuerySetManager(object):
|
||||
@@ -1904,7 +1932,7 @@ class QuerySetManager(object):
|
||||
return self
|
||||
|
||||
# owner is the document that contains the QuerySetManager
|
||||
queryset_class = owner._meta['queryset_class'] or QuerySet
|
||||
queryset_class = owner._meta.get('queryset_class') or QuerySet
|
||||
queryset = queryset_class(owner, owner._get_collection())
|
||||
if self.get_queryset:
|
||||
arg_count = self.get_queryset.func_code.co_argcount
|
||||
|
@@ -5,7 +5,7 @@
|
||||
%define srcname mongoengine
|
||||
|
||||
Name: python-%{srcname}
|
||||
Version: 0.6.20
|
||||
Version: 0.7rc1
|
||||
Release: 1%{?dist}
|
||||
Summary: A Python Document-Object Mapper for working with MongoDB
|
||||
|
||||
|
@@ -1,13 +1,11 @@
|
||||
[aliases]
|
||||
test = nosetests
|
||||
|
||||
[nosetests]
|
||||
verbosity = 2
|
||||
verbosity = 3
|
||||
detailed-errors = 1
|
||||
#with-coverage = 1
|
||||
#cover-erase = 1
|
||||
#cover-html = 1
|
||||
#cover-html-dir = ../htmlcov
|
||||
#cover-package = mongoengine
|
||||
py3where = build
|
||||
where = tests
|
||||
#tests = test_bugfix.py
|
||||
#tests = test_bugfix.py
|
46
setup.py
46
setup.py
@@ -1,5 +1,12 @@
|
||||
from setuptools import setup, find_packages
|
||||
import os
|
||||
import sys
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
# Hack to silence atexit traceback in newer python versions
|
||||
try:
|
||||
import multiprocessing
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
DESCRIPTION = "A Python Document-Object Mapper for working with MongoDB"
|
||||
|
||||
@@ -9,19 +16,20 @@ try:
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def get_version(version_tuple):
|
||||
version = '%s.%s' % (version_tuple[0], version_tuple[1])
|
||||
if version_tuple[2]:
|
||||
version = '%s.%s' % (version, version_tuple[2])
|
||||
return version
|
||||
if not isinstance(version_tuple[-1], int):
|
||||
return '.'.join(map(str, version_tuple[:-1])) + version_tuple[-1]
|
||||
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 = filter(lambda l: l.startswith('VERSION'), open(init))[0]
|
||||
version_line = list(filter(lambda l: l.startswith('VERSION'), open(init)))[0]
|
||||
|
||||
VERSION = get_version(eval(version_line.split('=')[-1]))
|
||||
print VERSION
|
||||
print(VERSION)
|
||||
|
||||
CLASSIFIERS = [
|
||||
'Development Status :: 4 - Beta',
|
||||
@@ -29,13 +37,32 @@ CLASSIFIERS = [
|
||||
'License :: OSI Approved :: MIT License',
|
||||
'Operating System :: OS Independent',
|
||||
'Programming Language :: Python',
|
||||
"Programming Language :: Python :: 2",
|
||||
"Programming Language :: Python :: 2.5",
|
||||
"Programming Language :: Python :: 2.6",
|
||||
"Programming Language :: Python :: 2.7",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.1",
|
||||
"Programming Language :: Python :: 3.2",
|
||||
"Programming Language :: Python :: Implementation :: CPython",
|
||||
'Topic :: Database',
|
||||
'Topic :: Software Development :: Libraries :: Python Modules',
|
||||
]
|
||||
|
||||
extra_opts = {}
|
||||
if sys.version_info[0] == 3:
|
||||
extra_opts['use_2to3'] = True
|
||||
extra_opts['tests_require'] = ['nose', 'coverage', 'blinker']
|
||||
extra_opts['packages'] = find_packages(exclude=('tests',))
|
||||
if "test" in sys.argv or "nosetests" in sys.argv:
|
||||
extra_opts['packages'].append("tests")
|
||||
extra_opts['package_data'] = {"tests": ["mongoengine.png"]}
|
||||
else:
|
||||
extra_opts['tests_require'] = ['nose', 'coverage', 'blinker', 'django>=1.3', 'PIL']
|
||||
extra_opts['packages'] = find_packages(exclude=('tests',))
|
||||
|
||||
setup(name='mongoengine',
|
||||
version=VERSION,
|
||||
packages=find_packages(exclude=('tests',)),
|
||||
author='Harry Marr',
|
||||
author_email='harry.marr@{nospam}gmail.com',
|
||||
maintainer="Ross Lawley",
|
||||
@@ -48,5 +75,6 @@ setup(name='mongoengine',
|
||||
platforms=['any'],
|
||||
classifiers=CLASSIFIERS,
|
||||
install_requires=['pymongo'],
|
||||
tests_require=['nose', 'coverage', 'blinker', 'django>=1.3', 'PIL']
|
||||
test_suite='nose.collector',
|
||||
**extra_opts
|
||||
)
|
||||
|
96
tests/test_all_warnings.py
Normal file
96
tests/test_all_warnings.py
Normal file
@@ -0,0 +1,96 @@
|
||||
import unittest
|
||||
import warnings
|
||||
|
||||
from mongoengine import *
|
||||
from mongoengine.tests import query_counter
|
||||
|
||||
|
||||
class TestWarnings(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
conn = 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})
|
||||
|
||||
def tearDown(self):
|
||||
# restore default handling of warnings
|
||||
warnings.showwarning = self.showwarning_default
|
||||
|
||||
def test_allow_inheritance_future_warning(self):
|
||||
"""Add FutureWarning for future allow_inhertiance default change.
|
||||
"""
|
||||
|
||||
class SimpleBase(Document):
|
||||
a = IntField()
|
||||
|
||||
class InheritedClass(SimpleBase):
|
||||
b = IntField()
|
||||
|
||||
InheritedClass()
|
||||
self.assertEqual(len(self.warning_list), 1)
|
||||
warning = self.warning_list[0]
|
||||
self.assertEqual(FutureWarning, warning["category"])
|
||||
self.assertTrue("InheritedClass" in str(warning["message"]))
|
||||
|
||||
def test_dbref_reference_field_future_warning(self):
|
||||
|
||||
class Person(Document):
|
||||
name = StringField()
|
||||
parent = ReferenceField('self')
|
||||
|
||||
Person.drop_collection()
|
||||
|
||||
p1 = Person()
|
||||
p1.parent = None
|
||||
p1.save()
|
||||
|
||||
p2 = Person(name="Wilson Jr")
|
||||
p2.parent = p1
|
||||
p2.save(cascade=False)
|
||||
|
||||
self.assertEqual(len(self.warning_list), 1)
|
||||
warning = self.warning_list[0]
|
||||
self.assertEqual(FutureWarning, warning["category"])
|
||||
self.assertTrue("ReferenceFields will default to using ObjectId"
|
||||
in str(warning["message"]))
|
||||
|
||||
def test_document_save_cascade_future_warning(self):
|
||||
|
||||
class Person(Document):
|
||||
name = StringField()
|
||||
parent = ReferenceField('self')
|
||||
|
||||
Person.drop_collection()
|
||||
|
||||
p1 = Person(name="Wilson Snr")
|
||||
p1.parent = None
|
||||
p1.save()
|
||||
|
||||
p2 = Person(name="Wilson Jr")
|
||||
p2.parent = p1
|
||||
p2.parent.name = "Poppa Wilson"
|
||||
p2.save()
|
||||
|
||||
self.assertEqual(len(self.warning_list), 1)
|
||||
warning = self.warning_list[0]
|
||||
self.assertEqual(FutureWarning, warning["category"])
|
||||
self.assertTrue("Cascading saves will default to off in 0.8"
|
||||
in str(warning["message"]))
|
||||
|
||||
def test_document_collection_syntax_warning(self):
|
||||
|
||||
class NonAbstractBase(Document):
|
||||
pass
|
||||
|
||||
class InheritedDocumentFailTest(NonAbstractBase):
|
||||
meta = {'collection': 'fail'}
|
||||
|
||||
warning = self.warning_list[0]
|
||||
self.assertEqual(SyntaxWarning, warning["category"])
|
||||
self.assertEqual('non_abstract_base',
|
||||
InheritedDocumentFailTest._get_collection_name())
|
@@ -1,5 +1,8 @@
|
||||
from __future__ import with_statement
|
||||
import unittest
|
||||
|
||||
from bson import DBRef
|
||||
|
||||
from mongoengine import *
|
||||
from mongoengine.connection import get_db
|
||||
from mongoengine.tests import query_counter
|
||||
@@ -63,6 +66,130 @@ class FieldTest(unittest.TestCase):
|
||||
User.drop_collection()
|
||||
Group.drop_collection()
|
||||
|
||||
def test_list_item_dereference_dref_false(self):
|
||||
"""Ensure that DBRef items in ListFields are dereferenced.
|
||||
"""
|
||||
class User(Document):
|
||||
name = StringField()
|
||||
|
||||
class Group(Document):
|
||||
members = ListField(ReferenceField(User, dbref=False))
|
||||
|
||||
User.drop_collection()
|
||||
Group.drop_collection()
|
||||
|
||||
for i in xrange(1, 51):
|
||||
user = User(name='user %s' % i)
|
||||
user.save()
|
||||
|
||||
group = Group(members=User.objects)
|
||||
group.save()
|
||||
|
||||
with query_counter() as q:
|
||||
self.assertEqual(q, 0)
|
||||
|
||||
group_obj = Group.objects.first()
|
||||
self.assertEqual(q, 1)
|
||||
|
||||
[m for m in group_obj.members]
|
||||
self.assertEqual(q, 2)
|
||||
|
||||
# Document select_related
|
||||
with query_counter() as q:
|
||||
self.assertEqual(q, 0)
|
||||
|
||||
group_obj = Group.objects.first().select_related()
|
||||
|
||||
self.assertEqual(q, 2)
|
||||
[m for m in group_obj.members]
|
||||
self.assertEqual(q, 2)
|
||||
|
||||
# Queryset select_related
|
||||
with query_counter() as q:
|
||||
self.assertEqual(q, 0)
|
||||
group_objs = Group.objects.select_related()
|
||||
self.assertEqual(q, 2)
|
||||
for group_obj in group_objs:
|
||||
[m for m in group_obj.members]
|
||||
self.assertEqual(q, 2)
|
||||
|
||||
User.drop_collection()
|
||||
Group.drop_collection()
|
||||
|
||||
def test_handle_old_style_references(self):
|
||||
"""Ensure that DBRef items in ListFields are dereferenced.
|
||||
"""
|
||||
class User(Document):
|
||||
name = StringField()
|
||||
|
||||
class Group(Document):
|
||||
members = ListField(ReferenceField(User, dbref=True))
|
||||
|
||||
User.drop_collection()
|
||||
Group.drop_collection()
|
||||
|
||||
for i in xrange(1, 26):
|
||||
user = User(name='user %s' % i)
|
||||
user.save()
|
||||
|
||||
group = Group(members=User.objects)
|
||||
group.save()
|
||||
|
||||
group = Group._get_collection().find_one()
|
||||
|
||||
# Update the model to change the reference
|
||||
class Group(Document):
|
||||
members = ListField(ReferenceField(User, dbref=False))
|
||||
|
||||
group = Group.objects.first()
|
||||
group.members.append(User(name="String!").save())
|
||||
group.save()
|
||||
|
||||
group = Group.objects.first()
|
||||
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
|
||||
"""
|
||||
|
||||
# Create some sample data
|
||||
class User(Document):
|
||||
name = StringField()
|
||||
|
||||
class Group(Document):
|
||||
author = ReferenceField(User, dbref=True)
|
||||
members = ListField(ReferenceField(User, dbref=True))
|
||||
|
||||
User.drop_collection()
|
||||
Group.drop_collection()
|
||||
|
||||
user = User(name="Ross").save()
|
||||
group = Group(author=user, members=[user]).save()
|
||||
|
||||
raw_data = Group._get_collection().find_one()
|
||||
self.assertTrue(isinstance(raw_data['author'], DBRef))
|
||||
self.assertTrue(isinstance(raw_data['members'][0], DBRef))
|
||||
|
||||
# Migrate the model definition
|
||||
class Group(Document):
|
||||
author = ReferenceField(User, dbref=False)
|
||||
members = ListField(ReferenceField(User, dbref=False))
|
||||
|
||||
# Migrate the data
|
||||
for g in Group.objects():
|
||||
g.author = g.author
|
||||
g.members = g.members
|
||||
g.save()
|
||||
|
||||
group = Group.objects.first()
|
||||
self.assertEqual(group.author, user)
|
||||
self.assertEqual(group.members, [user])
|
||||
|
||||
raw_data = Group._get_collection().find_one()
|
||||
self.assertTrue(isinstance(raw_data['author'], basestring))
|
||||
self.assertTrue(isinstance(raw_data['members'][0], basestring))
|
||||
|
||||
def test_recursive_reference(self):
|
||||
"""Ensure that ReferenceFields can reference their own documents.
|
||||
"""
|
||||
@@ -109,10 +236,10 @@ class FieldTest(unittest.TestCase):
|
||||
peter = Employee.objects.with_id(peter.id).select_related()
|
||||
self.assertEqual(q, 2)
|
||||
|
||||
self.assertEquals(peter.boss, bill)
|
||||
self.assertEqual(peter.boss, bill)
|
||||
self.assertEqual(q, 2)
|
||||
|
||||
self.assertEquals(peter.friends, friends)
|
||||
self.assertEqual(peter.friends, friends)
|
||||
self.assertEqual(q, 2)
|
||||
|
||||
# Queryset select_related
|
||||
@@ -123,10 +250,10 @@ class FieldTest(unittest.TestCase):
|
||||
self.assertEqual(q, 2)
|
||||
|
||||
for employee in employees:
|
||||
self.assertEquals(employee.boss, bill)
|
||||
self.assertEqual(employee.boss, bill)
|
||||
self.assertEqual(q, 2)
|
||||
|
||||
self.assertEquals(employee.friends, friends)
|
||||
self.assertEqual(employee.friends, friends)
|
||||
self.assertEqual(q, 2)
|
||||
|
||||
def test_circular_reference(self):
|
||||
@@ -160,7 +287,7 @@ class FieldTest(unittest.TestCase):
|
||||
daughter.relations.append(self_rel)
|
||||
daughter.save()
|
||||
|
||||
self.assertEquals("[<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
|
||||
@@ -186,7 +313,7 @@ class FieldTest(unittest.TestCase):
|
||||
daughter.relations.append(daughter)
|
||||
daughter.save()
|
||||
|
||||
self.assertEquals("[<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
|
||||
@@ -228,7 +355,7 @@ class FieldTest(unittest.TestCase):
|
||||
anna.other.name = "Anna's friends"
|
||||
anna.save()
|
||||
|
||||
self.assertEquals(
|
||||
self.assertEqual(
|
||||
"[<Person: Paul>, <Person: Maria>, <Person: Julia>, <Person: Anna>]",
|
||||
"%s" % Person.objects()
|
||||
)
|
||||
@@ -781,8 +908,8 @@ class FieldTest(unittest.TestCase):
|
||||
root.save()
|
||||
|
||||
root = root.reload()
|
||||
self.assertEquals(root.children, [company])
|
||||
self.assertEquals(company.parents, [root])
|
||||
self.assertEqual(root.children, [company])
|
||||
self.assertEqual(company.parents, [root])
|
||||
|
||||
def test_dict_in_dbref_instance(self):
|
||||
|
||||
@@ -808,8 +935,8 @@ class FieldTest(unittest.TestCase):
|
||||
room_101.save()
|
||||
|
||||
room = Room.objects.first().select_related()
|
||||
self.assertEquals(room.staffs_with_position[0]['staff'], sarah)
|
||||
self.assertEquals(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):
|
||||
@@ -839,8 +966,8 @@ class FieldTest(unittest.TestCase):
|
||||
foo.save()
|
||||
foo.reload()
|
||||
|
||||
self.assertEquals(type(foo.bar), Bar)
|
||||
self.assertEquals(type(foo.baz), Baz)
|
||||
self.assertEqual(type(foo.bar), Bar)
|
||||
self.assertEqual(type(foo.baz), Baz)
|
||||
|
||||
def test_list_lookup_not_checked_in_map(self):
|
||||
"""Ensure we dereference list data correctly
|
||||
@@ -862,4 +989,4 @@ class FieldTest(unittest.TestCase):
|
||||
|
||||
msg = Message.objects.get(id=1)
|
||||
self.assertEqual(0, msg.comments[0].id)
|
||||
self.assertEqual(1, msg.comments[1].id)
|
||||
self.assertEqual(1, msg.comments[1].id)
|
||||
|
@@ -1,24 +1,34 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import with_statement
|
||||
import unittest
|
||||
|
||||
from nose.plugins.skip import SkipTest
|
||||
from mongoengine.python_support import PY3
|
||||
from mongoengine import *
|
||||
from mongoengine.django.shortcuts import get_document_or_404
|
||||
|
||||
from django.http import Http404
|
||||
from django.template import Context, Template
|
||||
from django.conf import settings
|
||||
from django.core.paginator import Paginator
|
||||
try:
|
||||
from mongoengine.django.shortcuts import get_document_or_404
|
||||
|
||||
settings.configure()
|
||||
from django.http import Http404
|
||||
from django.template import Context, Template
|
||||
from django.conf import settings
|
||||
from django.core.paginator import Paginator
|
||||
|
||||
from django.contrib.sessions.tests import SessionTestsMixin
|
||||
from mongoengine.django.sessions import SessionStore, MongoSession
|
||||
settings.configure()
|
||||
|
||||
from django.contrib.sessions.tests import SessionTestsMixin
|
||||
from mongoengine.django.sessions import SessionStore, MongoSession
|
||||
except Exception, err:
|
||||
if PY3:
|
||||
SessionTestsMixin = type # dummy value so no error
|
||||
SessionStore = None # dummy value so no error
|
||||
else:
|
||||
raise err
|
||||
|
||||
|
||||
class QuerySetTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
if PY3:
|
||||
raise SkipTest('django does not have Python 3 support')
|
||||
connect(db='mongoenginetest')
|
||||
|
||||
class Person(Document):
|
||||
@@ -99,6 +109,8 @@ class MongoDBSessionTest(SessionTestsMixin, unittest.TestCase):
|
||||
backend = SessionStore
|
||||
|
||||
def setUp(self):
|
||||
if PY3:
|
||||
raise SkipTest('django does not have Python 3 support')
|
||||
connect(db='mongoenginetest')
|
||||
MongoSession.drop_collection()
|
||||
super(MongoDBSessionTest, self).setUp()
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -25,14 +25,14 @@ class DynamicDocTest(unittest.TestCase):
|
||||
p.name = "James"
|
||||
p.age = 34
|
||||
|
||||
self.assertEquals(p.to_mongo(),
|
||||
self.assertEqual(p.to_mongo(),
|
||||
{"_types": ["Person"], "_cls": "Person",
|
||||
"name": "James", "age": 34}
|
||||
)
|
||||
|
||||
p.save()
|
||||
|
||||
self.assertEquals(self.Person.objects.first().age, 34)
|
||||
self.assertEqual(self.Person.objects.first().age, 34)
|
||||
|
||||
# Confirm no changes to self.Person
|
||||
self.assertFalse(hasattr(self.Person, 'age'))
|
||||
@@ -40,11 +40,11 @@ class DynamicDocTest(unittest.TestCase):
|
||||
def test_dynamic_document_delta(self):
|
||||
"""Ensures simple dynamic documents can delta correctly"""
|
||||
p = self.Person(name="James", age=34)
|
||||
self.assertEquals(p._delta(), ({'_types': ['Person'], 'age': 34, 'name': 'James', '_cls': 'Person'}, {}))
|
||||
self.assertEqual(p._delta(), ({'_types': ['Person'], 'age': 34, 'name': 'James', '_cls': 'Person'}, {}))
|
||||
|
||||
p.doc = 123
|
||||
del(p.doc)
|
||||
self.assertEquals(p._delta(), ({'_types': ['Person'], 'age': 34, 'name': 'James', '_cls': 'Person'}, {'doc': 1}))
|
||||
self.assertEqual(p._delta(), ({'_types': ['Person'], 'age': 34, 'name': 'James', '_cls': 'Person'}, {'doc': 1}))
|
||||
|
||||
def test_change_scope_of_variable(self):
|
||||
"""Test changing the scope of a dynamic field has no adverse effects"""
|
||||
@@ -58,7 +58,7 @@ class DynamicDocTest(unittest.TestCase):
|
||||
p.save()
|
||||
|
||||
p = self.Person.objects.get()
|
||||
self.assertEquals(p.misc, {'hello': 'world'})
|
||||
self.assertEqual(p.misc, {'hello': 'world'})
|
||||
|
||||
def test_delete_dynamic_field(self):
|
||||
"""Test deleting a dynamic field works"""
|
||||
@@ -73,10 +73,10 @@ class DynamicDocTest(unittest.TestCase):
|
||||
p.save()
|
||||
|
||||
p = self.Person.objects.get()
|
||||
self.assertEquals(p.misc, {'hello': 'world'})
|
||||
self.assertEqual(p.misc, {'hello': 'world'})
|
||||
collection = self.db[self.Person._get_collection_name()]
|
||||
obj = collection.find_one()
|
||||
self.assertEquals(sorted(obj.keys()), ['_cls', '_id', '_types', 'misc', 'name'])
|
||||
self.assertEqual(sorted(obj.keys()), ['_cls', '_id', '_types', 'misc', 'name'])
|
||||
|
||||
del(p.misc)
|
||||
p.save()
|
||||
@@ -85,7 +85,7 @@ class DynamicDocTest(unittest.TestCase):
|
||||
self.assertFalse(hasattr(p, 'misc'))
|
||||
|
||||
obj = collection.find_one()
|
||||
self.assertEquals(sorted(obj.keys()), ['_cls', '_id', '_types', 'name'])
|
||||
self.assertEqual(sorted(obj.keys()), ['_cls', '_id', '_types', 'name'])
|
||||
|
||||
def test_dynamic_document_queries(self):
|
||||
"""Ensure we can query dynamic fields"""
|
||||
@@ -94,10 +94,10 @@ class DynamicDocTest(unittest.TestCase):
|
||||
p.age = 22
|
||||
p.save()
|
||||
|
||||
self.assertEquals(1, self.Person.objects(age=22).count())
|
||||
self.assertEqual(1, self.Person.objects(age=22).count())
|
||||
p = self.Person.objects(age=22)
|
||||
p = p.get()
|
||||
self.assertEquals(22, p.age)
|
||||
self.assertEqual(22, p.age)
|
||||
|
||||
def test_complex_dynamic_document_queries(self):
|
||||
class Person(DynamicDocument):
|
||||
@@ -117,8 +117,8 @@ class DynamicDocTest(unittest.TestCase):
|
||||
p2.age = 10
|
||||
p2.save()
|
||||
|
||||
self.assertEquals(Person.objects(age__icontains='ten').count(), 2)
|
||||
self.assertEquals(Person.objects(age__gte=10).count(), 1)
|
||||
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"""
|
||||
@@ -126,7 +126,7 @@ class DynamicDocTest(unittest.TestCase):
|
||||
p.misc = {'hello': 'world'}
|
||||
p.save()
|
||||
|
||||
self.assertEquals(1, self.Person.objects(misc__hello='world').count())
|
||||
self.assertEqual(1, self.Person.objects(misc__hello='world').count())
|
||||
|
||||
def test_inheritance(self):
|
||||
"""Ensure that dynamic document plays nice with inheritance"""
|
||||
@@ -146,8 +146,8 @@ class DynamicDocTest(unittest.TestCase):
|
||||
joe_bloggs.age = 20
|
||||
joe_bloggs.save()
|
||||
|
||||
self.assertEquals(1, self.Person.objects(age=20).count())
|
||||
self.assertEquals(1, Employee.objects(age=20).count())
|
||||
self.assertEqual(1, self.Person.objects(age=20).count())
|
||||
self.assertEqual(1, Employee.objects(age=20).count())
|
||||
|
||||
joe_bloggs = self.Person.objects.first()
|
||||
self.assertTrue(isinstance(joe_bloggs, Employee))
|
||||
@@ -170,7 +170,7 @@ class DynamicDocTest(unittest.TestCase):
|
||||
embedded_1.list_field = ['1', 2, {'hello': 'world'}]
|
||||
doc.embedded_field = embedded_1
|
||||
|
||||
self.assertEquals(doc.to_mongo(), {"_types": ['Doc'], "_cls": "Doc",
|
||||
self.assertEqual(doc.to_mongo(), {"_types": ['Doc'], "_cls": "Doc",
|
||||
"embedded_field": {
|
||||
"_types": ['Embedded'], "_cls": "Embedded",
|
||||
"string_field": "hello",
|
||||
@@ -182,11 +182,11 @@ class DynamicDocTest(unittest.TestCase):
|
||||
doc.save()
|
||||
|
||||
doc = Doc.objects.first()
|
||||
self.assertEquals(doc.embedded_field.__class__, Embedded)
|
||||
self.assertEquals(doc.embedded_field.string_field, "hello")
|
||||
self.assertEquals(doc.embedded_field.int_field, 1)
|
||||
self.assertEquals(doc.embedded_field.dict_field, {'hello': 'world'})
|
||||
self.assertEquals(doc.embedded_field.list_field, ['1', 2, {'hello': 'world'}])
|
||||
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"""
|
||||
@@ -213,7 +213,7 @@ class DynamicDocTest(unittest.TestCase):
|
||||
embedded_1.list_field = ['1', 2, embedded_2]
|
||||
doc.embedded_field = embedded_1
|
||||
|
||||
self.assertEquals(doc.to_mongo(), {"_types": ['Doc'], "_cls": "Doc",
|
||||
self.assertEqual(doc.to_mongo(), {"_types": ['Doc'], "_cls": "Doc",
|
||||
"embedded_field": {
|
||||
"_types": ['Embedded'], "_cls": "Embedded",
|
||||
"string_field": "hello",
|
||||
@@ -230,20 +230,20 @@ class DynamicDocTest(unittest.TestCase):
|
||||
})
|
||||
doc.save()
|
||||
doc = Doc.objects.first()
|
||||
self.assertEquals(doc.embedded_field.__class__, Embedded)
|
||||
self.assertEquals(doc.embedded_field.string_field, "hello")
|
||||
self.assertEquals(doc.embedded_field.int_field, 1)
|
||||
self.assertEquals(doc.embedded_field.dict_field, {'hello': 'world'})
|
||||
self.assertEquals(doc.embedded_field.list_field[0], '1')
|
||||
self.assertEquals(doc.embedded_field.list_field[1], 2)
|
||||
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.list_field[1], 2)
|
||||
|
||||
embedded_field = doc.embedded_field.list_field[2]
|
||||
|
||||
self.assertEquals(embedded_field.__class__, Embedded)
|
||||
self.assertEquals(embedded_field.string_field, "hello")
|
||||
self.assertEquals(embedded_field.int_field, 1)
|
||||
self.assertEquals(embedded_field.dict_field, {'hello': 'world'})
|
||||
self.assertEquals(embedded_field.list_field, ['1', 2, {'hello': 'world'}])
|
||||
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'}])
|
||||
|
||||
def test_delta_for_dynamic_documents(self):
|
||||
p = self.Person()
|
||||
@@ -252,18 +252,18 @@ class DynamicDocTest(unittest.TestCase):
|
||||
p.save()
|
||||
|
||||
p.age = 24
|
||||
self.assertEquals(p.age, 24)
|
||||
self.assertEquals(p._get_changed_fields(), ['age'])
|
||||
self.assertEquals(p._delta(), ({'age': 24}, {}))
|
||||
self.assertEqual(p.age, 24)
|
||||
self.assertEqual(p._get_changed_fields(), ['age'])
|
||||
self.assertEqual(p._delta(), ({'age': 24}, {}))
|
||||
|
||||
p = self.Person.objects(age=22).get()
|
||||
p.age = 24
|
||||
self.assertEquals(p.age, 24)
|
||||
self.assertEquals(p._get_changed_fields(), ['age'])
|
||||
self.assertEquals(p._delta(), ({'age': 24}, {}))
|
||||
self.assertEqual(p.age, 24)
|
||||
self.assertEqual(p._get_changed_fields(), ['age'])
|
||||
self.assertEqual(p._delta(), ({'age': 24}, {}))
|
||||
|
||||
p.save()
|
||||
self.assertEquals(1, self.Person.objects(age=24).count())
|
||||
self.assertEqual(1, self.Person.objects(age=24).count())
|
||||
|
||||
def test_delta(self):
|
||||
|
||||
@@ -275,40 +275,40 @@ class DynamicDocTest(unittest.TestCase):
|
||||
doc.save()
|
||||
|
||||
doc = Doc.objects.first()
|
||||
self.assertEquals(doc._get_changed_fields(), [])
|
||||
self.assertEquals(doc._delta(), ({}, {}))
|
||||
self.assertEqual(doc._get_changed_fields(), [])
|
||||
self.assertEqual(doc._delta(), ({}, {}))
|
||||
|
||||
doc.string_field = 'hello'
|
||||
self.assertEquals(doc._get_changed_fields(), ['string_field'])
|
||||
self.assertEquals(doc._delta(), ({'string_field': 'hello'}, {}))
|
||||
self.assertEqual(doc._get_changed_fields(), ['string_field'])
|
||||
self.assertEqual(doc._delta(), ({'string_field': 'hello'}, {}))
|
||||
|
||||
doc._changed_fields = []
|
||||
doc.int_field = 1
|
||||
self.assertEquals(doc._get_changed_fields(), ['int_field'])
|
||||
self.assertEquals(doc._delta(), ({'int_field': 1}, {}))
|
||||
self.assertEqual(doc._get_changed_fields(), ['int_field'])
|
||||
self.assertEqual(doc._delta(), ({'int_field': 1}, {}))
|
||||
|
||||
doc._changed_fields = []
|
||||
dict_value = {'hello': 'world', 'ping': 'pong'}
|
||||
doc.dict_field = dict_value
|
||||
self.assertEquals(doc._get_changed_fields(), ['dict_field'])
|
||||
self.assertEquals(doc._delta(), ({'dict_field': dict_value}, {}))
|
||||
self.assertEqual(doc._get_changed_fields(), ['dict_field'])
|
||||
self.assertEqual(doc._delta(), ({'dict_field': dict_value}, {}))
|
||||
|
||||
doc._changed_fields = []
|
||||
list_value = ['1', 2, {'hello': 'world'}]
|
||||
doc.list_field = list_value
|
||||
self.assertEquals(doc._get_changed_fields(), ['list_field'])
|
||||
self.assertEquals(doc._delta(), ({'list_field': list_value}, {}))
|
||||
self.assertEqual(doc._get_changed_fields(), ['list_field'])
|
||||
self.assertEqual(doc._delta(), ({'list_field': list_value}, {}))
|
||||
|
||||
# Test unsetting
|
||||
doc._changed_fields = []
|
||||
doc.dict_field = {}
|
||||
self.assertEquals(doc._get_changed_fields(), ['dict_field'])
|
||||
self.assertEquals(doc._delta(), ({}, {'dict_field': 1}))
|
||||
self.assertEqual(doc._get_changed_fields(), ['dict_field'])
|
||||
self.assertEqual(doc._delta(), ({}, {'dict_field': 1}))
|
||||
|
||||
doc._changed_fields = []
|
||||
doc.list_field = []
|
||||
self.assertEquals(doc._get_changed_fields(), ['list_field'])
|
||||
self.assertEquals(doc._delta(), ({}, {'list_field': 1}))
|
||||
self.assertEqual(doc._get_changed_fields(), ['list_field'])
|
||||
self.assertEqual(doc._delta(), ({}, {'list_field': 1}))
|
||||
|
||||
def test_delta_recursive(self):
|
||||
"""Testing deltaing works with dynamic documents"""
|
||||
@@ -323,8 +323,8 @@ class DynamicDocTest(unittest.TestCase):
|
||||
doc.save()
|
||||
|
||||
doc = Doc.objects.first()
|
||||
self.assertEquals(doc._get_changed_fields(), [])
|
||||
self.assertEquals(doc._delta(), ({}, {}))
|
||||
self.assertEqual(doc._get_changed_fields(), [])
|
||||
self.assertEqual(doc._delta(), ({}, {}))
|
||||
|
||||
embedded_1 = Embedded()
|
||||
embedded_1.string_field = 'hello'
|
||||
@@ -333,7 +333,7 @@ class DynamicDocTest(unittest.TestCase):
|
||||
embedded_1.list_field = ['1', 2, {'hello': 'world'}]
|
||||
doc.embedded_field = embedded_1
|
||||
|
||||
self.assertEquals(doc._get_changed_fields(), ['embedded_field'])
|
||||
self.assertEqual(doc._get_changed_fields(), ['embedded_field'])
|
||||
|
||||
embedded_delta = {
|
||||
'string_field': 'hello',
|
||||
@@ -341,28 +341,28 @@ class DynamicDocTest(unittest.TestCase):
|
||||
'dict_field': {'hello': 'world'},
|
||||
'list_field': ['1', 2, {'hello': 'world'}]
|
||||
}
|
||||
self.assertEquals(doc.embedded_field._delta(), (embedded_delta, {}))
|
||||
self.assertEqual(doc.embedded_field._delta(), (embedded_delta, {}))
|
||||
embedded_delta.update({
|
||||
'_types': ['Embedded'],
|
||||
'_cls': 'Embedded',
|
||||
})
|
||||
self.assertEquals(doc._delta(), ({'embedded_field': embedded_delta}, {}))
|
||||
self.assertEqual(doc._delta(), ({'embedded_field': embedded_delta}, {}))
|
||||
|
||||
doc.save()
|
||||
doc.reload()
|
||||
|
||||
doc.embedded_field.dict_field = {}
|
||||
self.assertEquals(doc._get_changed_fields(), ['embedded_field.dict_field'])
|
||||
self.assertEquals(doc.embedded_field._delta(), ({}, {'dict_field': 1}))
|
||||
self.assertEqual(doc._get_changed_fields(), ['embedded_field.dict_field'])
|
||||
self.assertEqual(doc.embedded_field._delta(), ({}, {'dict_field': 1}))
|
||||
|
||||
self.assertEquals(doc._delta(), ({}, {'embedded_field.dict_field': 1}))
|
||||
self.assertEqual(doc._delta(), ({}, {'embedded_field.dict_field': 1}))
|
||||
doc.save()
|
||||
doc.reload()
|
||||
|
||||
doc.embedded_field.list_field = []
|
||||
self.assertEquals(doc._get_changed_fields(), ['embedded_field.list_field'])
|
||||
self.assertEquals(doc.embedded_field._delta(), ({}, {'list_field': 1}))
|
||||
self.assertEquals(doc._delta(), ({}, {'embedded_field.list_field': 1}))
|
||||
self.assertEqual(doc._get_changed_fields(), ['embedded_field.list_field'])
|
||||
self.assertEqual(doc.embedded_field._delta(), ({}, {'list_field': 1}))
|
||||
self.assertEqual(doc._delta(), ({}, {'embedded_field.list_field': 1}))
|
||||
doc.save()
|
||||
doc.reload()
|
||||
|
||||
@@ -373,8 +373,8 @@ class DynamicDocTest(unittest.TestCase):
|
||||
embedded_2.list_field = ['1', 2, {'hello': 'world'}]
|
||||
|
||||
doc.embedded_field.list_field = ['1', 2, embedded_2]
|
||||
self.assertEquals(doc._get_changed_fields(), ['embedded_field.list_field'])
|
||||
self.assertEquals(doc.embedded_field._delta(), ({
|
||||
self.assertEqual(doc._get_changed_fields(), ['embedded_field.list_field'])
|
||||
self.assertEqual(doc.embedded_field._delta(), ({
|
||||
'list_field': ['1', 2, {
|
||||
'_cls': 'Embedded',
|
||||
'_types': ['Embedded'],
|
||||
@@ -385,7 +385,7 @@ class DynamicDocTest(unittest.TestCase):
|
||||
}]
|
||||
}, {}))
|
||||
|
||||
self.assertEquals(doc._delta(), ({
|
||||
self.assertEqual(doc._delta(), ({
|
||||
'embedded_field.list_field': ['1', 2, {
|
||||
'_cls': 'Embedded',
|
||||
'_types': ['Embedded'],
|
||||
@@ -398,25 +398,25 @@ class DynamicDocTest(unittest.TestCase):
|
||||
doc.save()
|
||||
doc.reload()
|
||||
|
||||
self.assertEquals(doc.embedded_field.list_field[2]._changed_fields, [])
|
||||
self.assertEquals(doc.embedded_field.list_field[0], '1')
|
||||
self.assertEquals(doc.embedded_field.list_field[1], 2)
|
||||
self.assertEqual(doc.embedded_field.list_field[2]._changed_fields, [])
|
||||
self.assertEqual(doc.embedded_field.list_field[0], '1')
|
||||
self.assertEqual(doc.embedded_field.list_field[1], 2)
|
||||
for k in doc.embedded_field.list_field[2]._fields:
|
||||
self.assertEquals(doc.embedded_field.list_field[2][k], embedded_2[k])
|
||||
self.assertEqual(doc.embedded_field.list_field[2][k], embedded_2[k])
|
||||
|
||||
doc.embedded_field.list_field[2].string_field = 'world'
|
||||
self.assertEquals(doc._get_changed_fields(), ['embedded_field.list_field.2.string_field'])
|
||||
self.assertEquals(doc.embedded_field._delta(), ({'list_field.2.string_field': 'world'}, {}))
|
||||
self.assertEquals(doc._delta(), ({'embedded_field.list_field.2.string_field': 'world'}, {}))
|
||||
self.assertEqual(doc._get_changed_fields(), ['embedded_field.list_field.2.string_field'])
|
||||
self.assertEqual(doc.embedded_field._delta(), ({'list_field.2.string_field': 'world'}, {}))
|
||||
self.assertEqual(doc._delta(), ({'embedded_field.list_field.2.string_field': 'world'}, {}))
|
||||
doc.save()
|
||||
doc.reload()
|
||||
self.assertEquals(doc.embedded_field.list_field[2].string_field, 'world')
|
||||
self.assertEqual(doc.embedded_field.list_field[2].string_field, 'world')
|
||||
|
||||
# Test multiple assignments
|
||||
doc.embedded_field.list_field[2].string_field = 'hello world'
|
||||
doc.embedded_field.list_field[2] = doc.embedded_field.list_field[2]
|
||||
self.assertEquals(doc._get_changed_fields(), ['embedded_field.list_field'])
|
||||
self.assertEquals(doc.embedded_field._delta(), ({
|
||||
self.assertEqual(doc._get_changed_fields(), ['embedded_field.list_field'])
|
||||
self.assertEqual(doc.embedded_field._delta(), ({
|
||||
'list_field': ['1', 2, {
|
||||
'_types': ['Embedded'],
|
||||
'_cls': 'Embedded',
|
||||
@@ -424,7 +424,7 @@ class DynamicDocTest(unittest.TestCase):
|
||||
'int_field': 1,
|
||||
'list_field': ['1', 2, {'hello': 'world'}],
|
||||
'dict_field': {'hello': 'world'}}]}, {}))
|
||||
self.assertEquals(doc._delta(), ({
|
||||
self.assertEqual(doc._delta(), ({
|
||||
'embedded_field.list_field': ['1', 2, {
|
||||
'_types': ['Embedded'],
|
||||
'_cls': 'Embedded',
|
||||
@@ -435,32 +435,32 @@ class DynamicDocTest(unittest.TestCase):
|
||||
]}, {}))
|
||||
doc.save()
|
||||
doc.reload()
|
||||
self.assertEquals(doc.embedded_field.list_field[2].string_field, 'hello world')
|
||||
self.assertEqual(doc.embedded_field.list_field[2].string_field, 'hello world')
|
||||
|
||||
# Test list native methods
|
||||
doc.embedded_field.list_field[2].list_field.pop(0)
|
||||
self.assertEquals(doc._delta(), ({'embedded_field.list_field.2.list_field': [2, {'hello': 'world'}]}, {}))
|
||||
self.assertEqual(doc._delta(), ({'embedded_field.list_field.2.list_field': [2, {'hello': 'world'}]}, {}))
|
||||
doc.save()
|
||||
doc.reload()
|
||||
|
||||
doc.embedded_field.list_field[2].list_field.append(1)
|
||||
self.assertEquals(doc._delta(), ({'embedded_field.list_field.2.list_field': [2, {'hello': 'world'}, 1]}, {}))
|
||||
self.assertEqual(doc._delta(), ({'embedded_field.list_field.2.list_field': [2, {'hello': 'world'}, 1]}, {}))
|
||||
doc.save()
|
||||
doc.reload()
|
||||
self.assertEquals(doc.embedded_field.list_field[2].list_field, [2, {'hello': 'world'}, 1])
|
||||
self.assertEqual(doc.embedded_field.list_field[2].list_field, [2, {'hello': 'world'}, 1])
|
||||
|
||||
doc.embedded_field.list_field[2].list_field.sort()
|
||||
doc.embedded_field.list_field[2].list_field.sort(key=str)# use str as a key to allow comparing uncomperable types
|
||||
doc.save()
|
||||
doc.reload()
|
||||
self.assertEquals(doc.embedded_field.list_field[2].list_field, [1, 2, {'hello': 'world'}])
|
||||
self.assertEqual(doc.embedded_field.list_field[2].list_field, [1, 2, {'hello': 'world'}])
|
||||
|
||||
del(doc.embedded_field.list_field[2].list_field[2]['hello'])
|
||||
self.assertEquals(doc._delta(), ({'embedded_field.list_field.2.list_field': [1, 2, {}]}, {}))
|
||||
self.assertEqual(doc._delta(), ({'embedded_field.list_field.2.list_field': [1, 2, {}]}, {}))
|
||||
doc.save()
|
||||
doc.reload()
|
||||
|
||||
del(doc.embedded_field.list_field[2].list_field)
|
||||
self.assertEquals(doc._delta(), ({}, {'embedded_field.list_field.2.list_field': 1}))
|
||||
self.assertEqual(doc._delta(), ({}, {'embedded_field.list_field.2.list_field': 1}))
|
||||
|
||||
doc.save()
|
||||
doc.reload()
|
||||
@@ -470,8 +470,8 @@ class DynamicDocTest(unittest.TestCase):
|
||||
doc.reload()
|
||||
|
||||
doc.dict_field['embedded'].string_field = 'Hello World'
|
||||
self.assertEquals(doc._get_changed_fields(), ['dict_field.embedded.string_field'])
|
||||
self.assertEquals(doc._delta(), ({'dict_field.embedded.string_field': 'Hello World'}, {}))
|
||||
self.assertEqual(doc._get_changed_fields(), ['dict_field.embedded.string_field'])
|
||||
self.assertEqual(doc._delta(), ({'dict_field.embedded.string_field': 'Hello World'}, {}))
|
||||
|
||||
def test_indexes(self):
|
||||
"""Ensure that indexes are used when meta[indexes] is specified.
|
||||
@@ -500,3 +500,34 @@ class DynamicDocTest(unittest.TestCase):
|
||||
self.assertTrue([('_types', 1), ('category', 1), ('date', -1)]
|
||||
in info)
|
||||
self.assertTrue([('_types', 1), ('date', -1)] in info)
|
||||
|
||||
def test_dynamic_and_embedded(self):
|
||||
"""Ensure embedded documents play nicely"""
|
||||
|
||||
class Address(EmbeddedDocument):
|
||||
city = StringField()
|
||||
|
||||
class Person(DynamicDocument):
|
||||
name = StringField()
|
||||
meta = {'allow_inheritance': True}
|
||||
|
||||
Person.drop_collection()
|
||||
|
||||
Person(name="Ross", address=Address(city="London")).save()
|
||||
|
||||
person = Person.objects.first()
|
||||
person.address.city = "Lundenne"
|
||||
person.save()
|
||||
|
||||
self.assertEqual(Person.objects.first().address.city, "Lundenne")
|
||||
|
||||
person = Person.objects.first()
|
||||
person.address = Address(city="Londinium")
|
||||
person.save()
|
||||
|
||||
self.assertEqual(Person.objects.first().address.city, "Londinium")
|
||||
|
||||
person = Person.objects.first()
|
||||
person.age = 35
|
||||
person.save()
|
||||
self.assertEqual(Person.objects.first().age, 35)
|
||||
|
@@ -1,21 +1,23 @@
|
||||
from __future__ import with_statement
|
||||
import datetime
|
||||
import os
|
||||
import unittest
|
||||
import uuid
|
||||
import StringIO
|
||||
import tempfile
|
||||
import gridfs
|
||||
|
||||
from bson import Binary
|
||||
from decimal import Decimal
|
||||
|
||||
from bson import Binary, DBRef
|
||||
import gridfs
|
||||
|
||||
from nose.plugins.skip import SkipTest
|
||||
from mongoengine import *
|
||||
from mongoengine.connection import get_db
|
||||
from mongoengine.base import _document_registry, NotRegistered
|
||||
from mongoengine.python_support import PY3, b, StringIO, bin_type
|
||||
|
||||
TEST_IMAGE_PATH = os.path.join(os.path.dirname(__file__), 'mongoengine.png')
|
||||
|
||||
|
||||
class FieldTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
@@ -124,7 +126,7 @@ class FieldTest(unittest.TestCase):
|
||||
self.assertEqual(ret.int_fld, None)
|
||||
self.assertEqual(ret.flt_fld, None)
|
||||
# Return current time if retrived value is None.
|
||||
self.assert_(isinstance(ret.comp_dt_fld, datetime.datetime))
|
||||
self.assertTrue(isinstance(ret.comp_dt_fld, datetime.datetime))
|
||||
|
||||
self.assertRaises(ValidationError, ret.validate)
|
||||
|
||||
@@ -358,7 +360,7 @@ class FieldTest(unittest.TestCase):
|
||||
log.date = datetime.date.today()
|
||||
log.save()
|
||||
log.reload()
|
||||
self.assertEquals(log.date.date(), datetime.date.today())
|
||||
self.assertEqual(log.date.date(), datetime.date.today())
|
||||
|
||||
LogEntry.drop_collection()
|
||||
|
||||
@@ -369,8 +371,8 @@ class FieldTest(unittest.TestCase):
|
||||
log.date = d1
|
||||
log.save()
|
||||
log.reload()
|
||||
self.assertNotEquals(log.date, d1)
|
||||
self.assertEquals(log.date, d2)
|
||||
self.assertNotEqual(log.date, d1)
|
||||
self.assertEqual(log.date, d2)
|
||||
|
||||
# Post UTC - microseconds are rounded (down) nearest millisecond
|
||||
d1 = datetime.datetime(1970, 01, 01, 00, 00, 01, 9999)
|
||||
@@ -378,17 +380,19 @@ class FieldTest(unittest.TestCase):
|
||||
log.date = d1
|
||||
log.save()
|
||||
log.reload()
|
||||
self.assertNotEquals(log.date, d1)
|
||||
self.assertEquals(log.date, d2)
|
||||
self.assertNotEqual(log.date, d1)
|
||||
self.assertEqual(log.date, d2)
|
||||
|
||||
# Pre UTC dates microseconds below 1000 are dropped
|
||||
d1 = datetime.datetime(1969, 12, 31, 23, 59, 59, 999)
|
||||
d2 = datetime.datetime(1969, 12, 31, 23, 59, 59)
|
||||
log.date = d1
|
||||
log.save()
|
||||
log.reload()
|
||||
self.assertNotEquals(log.date, d1)
|
||||
self.assertEquals(log.date, d2)
|
||||
if not PY3:
|
||||
# Pre UTC dates microseconds below 1000 are dropped
|
||||
# This does not seem to be true in PY3
|
||||
d1 = datetime.datetime(1969, 12, 31, 23, 59, 59, 999)
|
||||
d2 = datetime.datetime(1969, 12, 31, 23, 59, 59)
|
||||
log.date = d1
|
||||
log.save()
|
||||
log.reload()
|
||||
self.assertNotEqual(log.date, d1)
|
||||
self.assertEqual(log.date, d2)
|
||||
|
||||
LogEntry.drop_collection()
|
||||
|
||||
@@ -407,21 +411,21 @@ class FieldTest(unittest.TestCase):
|
||||
log.date = d1
|
||||
log.save()
|
||||
log.reload()
|
||||
self.assertEquals(log.date, d1)
|
||||
self.assertEqual(log.date, d1)
|
||||
|
||||
# Post UTC - microseconds are rounded (down) nearest millisecond - with default datetimefields
|
||||
d1 = datetime.datetime(1970, 01, 01, 00, 00, 01, 9999)
|
||||
log.date = d1
|
||||
log.save()
|
||||
log.reload()
|
||||
self.assertEquals(log.date, d1)
|
||||
self.assertEqual(log.date, d1)
|
||||
|
||||
# Pre UTC dates microseconds below 1000 are dropped - with default datetimefields
|
||||
d1 = datetime.datetime(1969, 12, 31, 23, 59, 59, 999)
|
||||
log.date = d1
|
||||
log.save()
|
||||
log.reload()
|
||||
self.assertEquals(log.date, d1)
|
||||
self.assertEqual(log.date, d1)
|
||||
|
||||
# Pre UTC microseconds above 1000 is wonky - with default datetimefields
|
||||
# log.date has an invalid microsecond value so I can't construct
|
||||
@@ -431,7 +435,7 @@ class FieldTest(unittest.TestCase):
|
||||
log.date = d1
|
||||
log.save()
|
||||
log.reload()
|
||||
self.assertEquals(log.date, d1)
|
||||
self.assertEqual(log.date, d1)
|
||||
log1 = LogEntry.objects.get(date=d1)
|
||||
self.assertEqual(log, log1)
|
||||
|
||||
@@ -452,7 +456,7 @@ class FieldTest(unittest.TestCase):
|
||||
log.save()
|
||||
|
||||
log1 = LogEntry.objects.get(date=d1)
|
||||
self.assertEquals(log, log1)
|
||||
self.assertEqual(log, log1)
|
||||
|
||||
LogEntry.drop_collection()
|
||||
|
||||
@@ -640,13 +644,13 @@ class FieldTest(unittest.TestCase):
|
||||
post.info = [{'test': 3}]
|
||||
post.save()
|
||||
|
||||
self.assertEquals(BlogPost.objects.count(), 3)
|
||||
self.assertEquals(BlogPost.objects.filter(info__exact='test').count(), 1)
|
||||
self.assertEquals(BlogPost.objects.filter(info__0__test='test').count(), 1)
|
||||
self.assertEqual(BlogPost.objects.count(), 3)
|
||||
self.assertEqual(BlogPost.objects.filter(info__exact='test').count(), 1)
|
||||
self.assertEqual(BlogPost.objects.filter(info__0__test='test').count(), 1)
|
||||
|
||||
# Confirm handles non strings or non existing keys
|
||||
self.assertEquals(BlogPost.objects.filter(info__0__test__exact='5').count(), 0)
|
||||
self.assertEquals(BlogPost.objects.filter(info__100__test__exact='test').count(), 0)
|
||||
self.assertEqual(BlogPost.objects.filter(info__0__test__exact='5').count(), 0)
|
||||
self.assertEqual(BlogPost.objects.filter(info__100__test__exact='test').count(), 0)
|
||||
BlogPost.drop_collection()
|
||||
|
||||
def test_list_field_passed_in_value(self):
|
||||
@@ -661,7 +665,7 @@ class FieldTest(unittest.TestCase):
|
||||
|
||||
foo = Foo(bars=[])
|
||||
foo.bars.append(bar)
|
||||
self.assertEquals(repr(foo.bars), '[<Bar: Bar object>]')
|
||||
self.assertEqual(repr(foo.bars), '[<Bar: Bar object>]')
|
||||
|
||||
|
||||
def test_list_field_strict(self):
|
||||
@@ -746,20 +750,20 @@ class FieldTest(unittest.TestCase):
|
||||
self.assertTrue(isinstance(e2.mapping[1], IntegerSetting))
|
||||
|
||||
# Test querying
|
||||
self.assertEquals(Simple.objects.filter(mapping__1__value=42).count(), 1)
|
||||
self.assertEquals(Simple.objects.filter(mapping__2__number=1).count(), 1)
|
||||
self.assertEquals(Simple.objects.filter(mapping__2__complex__value=42).count(), 1)
|
||||
self.assertEquals(Simple.objects.filter(mapping__2__list__0__value=42).count(), 1)
|
||||
self.assertEquals(Simple.objects.filter(mapping__2__list__1__value='foo').count(), 1)
|
||||
self.assertEqual(Simple.objects.filter(mapping__1__value=42).count(), 1)
|
||||
self.assertEqual(Simple.objects.filter(mapping__2__number=1).count(), 1)
|
||||
self.assertEqual(Simple.objects.filter(mapping__2__complex__value=42).count(), 1)
|
||||
self.assertEqual(Simple.objects.filter(mapping__2__list__0__value=42).count(), 1)
|
||||
self.assertEqual(Simple.objects.filter(mapping__2__list__1__value='foo').count(), 1)
|
||||
|
||||
# Confirm can update
|
||||
Simple.objects().update(set__mapping__1=IntegerSetting(value=10))
|
||||
self.assertEquals(Simple.objects.filter(mapping__1__value=10).count(), 1)
|
||||
self.assertEqual(Simple.objects.filter(mapping__1__value=10).count(), 1)
|
||||
|
||||
Simple.objects().update(
|
||||
set__mapping__2__list__1=StringSetting(value='Boo'))
|
||||
self.assertEquals(Simple.objects.filter(mapping__2__list__1__value='foo').count(), 0)
|
||||
self.assertEquals(Simple.objects.filter(mapping__2__list__1__value='Boo').count(), 1)
|
||||
self.assertEqual(Simple.objects.filter(mapping__2__list__1__value='foo').count(), 0)
|
||||
self.assertEqual(Simple.objects.filter(mapping__2__list__1__value='Boo').count(), 1)
|
||||
|
||||
Simple.drop_collection()
|
||||
|
||||
@@ -798,19 +802,19 @@ class FieldTest(unittest.TestCase):
|
||||
post.info = {'details': {'test': 3}}
|
||||
post.save()
|
||||
|
||||
self.assertEquals(BlogPost.objects.count(), 3)
|
||||
self.assertEquals(BlogPost.objects.filter(info__title__exact='test').count(), 1)
|
||||
self.assertEquals(BlogPost.objects.filter(info__details__test__exact='test').count(), 1)
|
||||
self.assertEqual(BlogPost.objects.count(), 3)
|
||||
self.assertEqual(BlogPost.objects.filter(info__title__exact='test').count(), 1)
|
||||
self.assertEqual(BlogPost.objects.filter(info__details__test__exact='test').count(), 1)
|
||||
|
||||
# Confirm handles non strings or non existing keys
|
||||
self.assertEquals(BlogPost.objects.filter(info__details__test__exact=5).count(), 0)
|
||||
self.assertEquals(BlogPost.objects.filter(info__made_up__test__exact='test').count(), 0)
|
||||
self.assertEqual(BlogPost.objects.filter(info__details__test__exact=5).count(), 0)
|
||||
self.assertEqual(BlogPost.objects.filter(info__made_up__test__exact='test').count(), 0)
|
||||
|
||||
post = BlogPost.objects.create(info={'title': 'original'})
|
||||
post.info.update({'title': 'updated'})
|
||||
post.save()
|
||||
post.reload()
|
||||
self.assertEquals('updated', post.info['title'])
|
||||
self.assertEqual('updated', post.info['title'])
|
||||
|
||||
BlogPost.drop_collection()
|
||||
|
||||
@@ -863,19 +867,19 @@ class FieldTest(unittest.TestCase):
|
||||
self.assertTrue(isinstance(e2.mapping['someint'], IntegerSetting))
|
||||
|
||||
# Test querying
|
||||
self.assertEquals(Simple.objects.filter(mapping__someint__value=42).count(), 1)
|
||||
self.assertEquals(Simple.objects.filter(mapping__nested_dict__number=1).count(), 1)
|
||||
self.assertEquals(Simple.objects.filter(mapping__nested_dict__complex__value=42).count(), 1)
|
||||
self.assertEquals(Simple.objects.filter(mapping__nested_dict__list__0__value=42).count(), 1)
|
||||
self.assertEquals(Simple.objects.filter(mapping__nested_dict__list__1__value='foo').count(), 1)
|
||||
self.assertEqual(Simple.objects.filter(mapping__someint__value=42).count(), 1)
|
||||
self.assertEqual(Simple.objects.filter(mapping__nested_dict__number=1).count(), 1)
|
||||
self.assertEqual(Simple.objects.filter(mapping__nested_dict__complex__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)
|
||||
|
||||
# Confirm can update
|
||||
Simple.objects().update(
|
||||
set__mapping={"someint": IntegerSetting(value=10)})
|
||||
Simple.objects().update(
|
||||
set__mapping__nested_dict__list__1=StringSetting(value='Boo'))
|
||||
self.assertEquals(Simple.objects.filter(mapping__nested_dict__list__1__value='foo').count(), 0)
|
||||
self.assertEquals(Simple.objects.filter(mapping__nested_dict__list__1__value='Boo').count(), 1)
|
||||
self.assertEqual(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.drop_collection()
|
||||
|
||||
@@ -1082,6 +1086,42 @@ class FieldTest(unittest.TestCase):
|
||||
User.drop_collection()
|
||||
BlogPost.drop_collection()
|
||||
|
||||
def test_dbref_reference_fields(self):
|
||||
|
||||
class Person(Document):
|
||||
name = StringField()
|
||||
parent = ReferenceField('self', dbref=True)
|
||||
|
||||
Person.drop_collection()
|
||||
|
||||
p1 = Person(name="John").save()
|
||||
Person(name="Ross", parent=p1).save()
|
||||
|
||||
col = Person._get_collection()
|
||||
data = col.find_one({'name': 'Ross'})
|
||||
self.assertEqual(data['parent'], DBRef('person', p1.pk))
|
||||
|
||||
p = Person.objects.get(name="Ross")
|
||||
self.assertEqual(p.parent, p1)
|
||||
|
||||
def test_str_reference_fields(self):
|
||||
|
||||
class Person(Document):
|
||||
name = StringField()
|
||||
parent = ReferenceField('self', dbref=False)
|
||||
|
||||
Person.drop_collection()
|
||||
|
||||
p1 = Person(name="John").save()
|
||||
Person(name="Ross", parent=p1).save()
|
||||
|
||||
col = Person._get_collection()
|
||||
data = col.find_one({'name': 'Ross'})
|
||||
self.assertEqual(data['parent'], "%s" % p1.pk)
|
||||
|
||||
p = Person.objects.get(name="Ross")
|
||||
self.assertEqual(p.parent, p1)
|
||||
|
||||
def test_list_item_dereference(self):
|
||||
"""Ensure that DBRef items in ListFields are dereferenced.
|
||||
"""
|
||||
@@ -1118,6 +1158,7 @@ class FieldTest(unittest.TestCase):
|
||||
boss = ReferenceField('self')
|
||||
friends = ListField(ReferenceField('self'))
|
||||
|
||||
Employee.drop_collection()
|
||||
bill = Employee(name='Bill Lumbergh')
|
||||
bill.save()
|
||||
|
||||
@@ -1241,7 +1282,41 @@ class FieldTest(unittest.TestCase):
|
||||
|
||||
class BlogPost(Document):
|
||||
title = StringField()
|
||||
author = ReferenceField(Member)
|
||||
author = ReferenceField(Member, dbref=False)
|
||||
|
||||
Member.drop_collection()
|
||||
BlogPost.drop_collection()
|
||||
|
||||
m1 = Member(user_num=1)
|
||||
m1.save()
|
||||
m2 = Member(user_num=2)
|
||||
m2.save()
|
||||
|
||||
post1 = BlogPost(title='post 1', author=m1)
|
||||
post1.save()
|
||||
|
||||
post2 = BlogPost(title='post 2', author=m2)
|
||||
post2.save()
|
||||
|
||||
post = BlogPost.objects(author=m1).first()
|
||||
self.assertEqual(post.id, post1.id)
|
||||
|
||||
post = BlogPost.objects(author=m2).first()
|
||||
self.assertEqual(post.id, post2.id)
|
||||
|
||||
Member.drop_collection()
|
||||
BlogPost.drop_collection()
|
||||
|
||||
def test_reference_query_conversion_dbref(self):
|
||||
"""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)
|
||||
|
||||
class BlogPost(Document):
|
||||
title = StringField()
|
||||
author = ReferenceField(Member, dbref=True)
|
||||
|
||||
Member.drop_collection()
|
||||
BlogPost.drop_collection()
|
||||
@@ -1385,7 +1460,7 @@ class FieldTest(unittest.TestCase):
|
||||
Person.drop_collection()
|
||||
Person(name="Wilson Jr").save()
|
||||
|
||||
self.assertEquals(repr(Person.objects(city=None)),
|
||||
self.assertEqual(repr(Person.objects(city=None)),
|
||||
"[<Person: Person object>]")
|
||||
|
||||
|
||||
@@ -1463,7 +1538,7 @@ class FieldTest(unittest.TestCase):
|
||||
content_type = StringField()
|
||||
blob = BinaryField()
|
||||
|
||||
BLOB = '\xe6\x00\xc4\xff\x07'
|
||||
BLOB = b('\xe6\x00\xc4\xff\x07')
|
||||
MIME_TYPE = 'application/octet-stream'
|
||||
|
||||
Attachment.drop_collection()
|
||||
@@ -1473,7 +1548,7 @@ class FieldTest(unittest.TestCase):
|
||||
|
||||
attachment_1 = Attachment.objects().first()
|
||||
self.assertEqual(MIME_TYPE, attachment_1.content_type)
|
||||
self.assertEqual(BLOB, str(attachment_1.blob))
|
||||
self.assertEqual(BLOB, bin_type(attachment_1.blob))
|
||||
|
||||
Attachment.drop_collection()
|
||||
|
||||
@@ -1500,12 +1575,12 @@ class FieldTest(unittest.TestCase):
|
||||
|
||||
attachment_required = AttachmentRequired()
|
||||
self.assertRaises(ValidationError, attachment_required.validate)
|
||||
attachment_required.blob = Binary('\xe6\x00\xc4\xff\x07')
|
||||
attachment_required.blob = Binary(b('\xe6\x00\xc4\xff\x07'))
|
||||
attachment_required.validate()
|
||||
|
||||
attachment_size_limit = AttachmentSizeLimit(blob='\xe6\x00\xc4\xff\x07')
|
||||
attachment_size_limit = AttachmentSizeLimit(blob=b('\xe6\x00\xc4\xff\x07'))
|
||||
self.assertRaises(ValidationError, attachment_size_limit.validate)
|
||||
attachment_size_limit.blob = '\xe6\x00\xc4\xff'
|
||||
attachment_size_limit.blob = b('\xe6\x00\xc4\xff')
|
||||
attachment_size_limit.validate()
|
||||
|
||||
Attachment.drop_collection()
|
||||
@@ -1632,8 +1707,8 @@ class FieldTest(unittest.TestCase):
|
||||
class SetFile(Document):
|
||||
the_file = FileField()
|
||||
|
||||
text = 'Hello, World!'
|
||||
more_text = 'Foo Bar'
|
||||
text = b('Hello, World!')
|
||||
more_text = b('Foo Bar')
|
||||
content_type = 'text/plain'
|
||||
|
||||
PutFile.drop_collection()
|
||||
@@ -1646,14 +1721,14 @@ class FieldTest(unittest.TestCase):
|
||||
putfile.validate()
|
||||
result = PutFile.objects.first()
|
||||
self.assertTrue(putfile == result)
|
||||
self.assertEquals(result.the_file.read(), text)
|
||||
self.assertEquals(result.the_file.content_type, content_type)
|
||||
self.assertEqual(result.the_file.read(), text)
|
||||
self.assertEqual(result.the_file.content_type, content_type)
|
||||
result.the_file.delete() # Remove file from GridFS
|
||||
PutFile.objects.delete()
|
||||
|
||||
# Ensure file-like objects are stored
|
||||
putfile = PutFile()
|
||||
putstring = StringIO.StringIO()
|
||||
putstring = StringIO()
|
||||
putstring.write(text)
|
||||
putstring.seek(0)
|
||||
putfile.the_file.put(putstring, content_type=content_type)
|
||||
@@ -1661,8 +1736,8 @@ class FieldTest(unittest.TestCase):
|
||||
putfile.validate()
|
||||
result = PutFile.objects.first()
|
||||
self.assertTrue(putfile == result)
|
||||
self.assertEquals(result.the_file.read(), text)
|
||||
self.assertEquals(result.the_file.content_type, content_type)
|
||||
self.assertEqual(result.the_file.read(), text)
|
||||
self.assertEqual(result.the_file.content_type, content_type)
|
||||
result.the_file.delete()
|
||||
|
||||
streamfile = StreamFile()
|
||||
@@ -1674,14 +1749,14 @@ class FieldTest(unittest.TestCase):
|
||||
streamfile.validate()
|
||||
result = StreamFile.objects.first()
|
||||
self.assertTrue(streamfile == result)
|
||||
self.assertEquals(result.the_file.read(), text + more_text)
|
||||
self.assertEquals(result.the_file.content_type, content_type)
|
||||
self.assertEqual(result.the_file.read(), text + more_text)
|
||||
self.assertEqual(result.the_file.content_type, content_type)
|
||||
result.the_file.seek(0)
|
||||
self.assertEquals(result.the_file.tell(), 0)
|
||||
self.assertEquals(result.the_file.read(len(text)), text)
|
||||
self.assertEquals(result.the_file.tell(), len(text))
|
||||
self.assertEquals(result.the_file.read(len(more_text)), more_text)
|
||||
self.assertEquals(result.the_file.tell(), len(text + more_text))
|
||||
self.assertEqual(result.the_file.tell(), 0)
|
||||
self.assertEqual(result.the_file.read(len(text)), text)
|
||||
self.assertEqual(result.the_file.tell(), len(text))
|
||||
self.assertEqual(result.the_file.read(len(more_text)), more_text)
|
||||
self.assertEqual(result.the_file.tell(), len(text + more_text))
|
||||
result.the_file.delete()
|
||||
|
||||
# Ensure deleted file returns None
|
||||
@@ -1693,7 +1768,7 @@ class FieldTest(unittest.TestCase):
|
||||
setfile.validate()
|
||||
result = SetFile.objects.first()
|
||||
self.assertTrue(setfile == result)
|
||||
self.assertEquals(result.the_file.read(), text)
|
||||
self.assertEqual(result.the_file.read(), text)
|
||||
|
||||
# Try replacing file with new one
|
||||
result.the_file.replace(more_text)
|
||||
@@ -1701,7 +1776,7 @@ class FieldTest(unittest.TestCase):
|
||||
result.validate()
|
||||
result = SetFile.objects.first()
|
||||
self.assertTrue(setfile == result)
|
||||
self.assertEquals(result.the_file.read(), more_text)
|
||||
self.assertEqual(result.the_file.read(), more_text)
|
||||
result.the_file.delete()
|
||||
|
||||
PutFile.drop_collection()
|
||||
@@ -1722,7 +1797,7 @@ class FieldTest(unittest.TestCase):
|
||||
GridDocument.drop_collection()
|
||||
|
||||
with tempfile.TemporaryFile() as f:
|
||||
f.write("Hello World!")
|
||||
f.write(b("Hello World!"))
|
||||
f.flush()
|
||||
|
||||
# Test without default
|
||||
@@ -1733,28 +1808,28 @@ class FieldTest(unittest.TestCase):
|
||||
doc_b = GridDocument.objects.with_id(doc_a.id)
|
||||
doc_b.the_file.replace(f, filename='doc_b')
|
||||
doc_b.save()
|
||||
self.assertNotEquals(doc_b.the_file.grid_id, None)
|
||||
self.assertNotEqual(doc_b.the_file.grid_id, None)
|
||||
|
||||
# Test it matches
|
||||
doc_c = GridDocument.objects.with_id(doc_b.id)
|
||||
self.assertEquals(doc_b.the_file.grid_id, doc_c.the_file.grid_id)
|
||||
self.assertEqual(doc_b.the_file.grid_id, doc_c.the_file.grid_id)
|
||||
|
||||
# Test with default
|
||||
doc_d = GridDocument(the_file='')
|
||||
doc_d = GridDocument(the_file=b(''))
|
||||
doc_d.save()
|
||||
|
||||
doc_e = GridDocument.objects.with_id(doc_d.id)
|
||||
self.assertEquals(doc_d.the_file.grid_id, doc_e.the_file.grid_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.save()
|
||||
|
||||
doc_f = GridDocument.objects.with_id(doc_e.id)
|
||||
self.assertEquals(doc_e.the_file.grid_id, doc_f.the_file.grid_id)
|
||||
self.assertEqual(doc_e.the_file.grid_id, doc_f.the_file.grid_id)
|
||||
|
||||
db = GridDocument._get_db()
|
||||
grid_fs = gridfs.GridFS(db)
|
||||
self.assertEquals(['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
|
||||
@@ -1766,7 +1841,7 @@ class FieldTest(unittest.TestCase):
|
||||
# First instance
|
||||
test_file = TestFile()
|
||||
test_file.name = "Hello, World!"
|
||||
test_file.the_file.put('Hello, World!')
|
||||
test_file.the_file.put(b('Hello, World!'))
|
||||
test_file.save()
|
||||
|
||||
# Second instance
|
||||
@@ -1786,7 +1861,7 @@ class FieldTest(unittest.TestCase):
|
||||
|
||||
test_file = TestFile()
|
||||
self.assertFalse(bool(test_file.the_file))
|
||||
test_file.the_file = 'Hello, World!'
|
||||
test_file.the_file = b('Hello, World!')
|
||||
test_file.the_file.content_type = 'text/plain'
|
||||
test_file.save()
|
||||
self.assertTrue(bool(test_file.the_file))
|
||||
@@ -1802,6 +1877,8 @@ class FieldTest(unittest.TestCase):
|
||||
self.assertFalse(test_file.the_file in [{"test": 1}])
|
||||
|
||||
def test_image_field(self):
|
||||
if PY3:
|
||||
raise SkipTest('PIL does not have Python 3 support')
|
||||
|
||||
class TestImage(Document):
|
||||
image = ImageField()
|
||||
@@ -1814,15 +1891,17 @@ class FieldTest(unittest.TestCase):
|
||||
|
||||
t = TestImage.objects.first()
|
||||
|
||||
self.assertEquals(t.image.format, 'PNG')
|
||||
self.assertEqual(t.image.format, 'PNG')
|
||||
|
||||
w, h = t.image.size
|
||||
self.assertEquals(w, 371)
|
||||
self.assertEquals(h, 76)
|
||||
self.assertEqual(w, 371)
|
||||
self.assertEqual(h, 76)
|
||||
|
||||
t.image.delete()
|
||||
|
||||
def test_image_field_resize(self):
|
||||
if PY3:
|
||||
raise SkipTest('PIL does not have Python 3 support')
|
||||
|
||||
class TestImage(Document):
|
||||
image = ImageField(size=(185, 37))
|
||||
@@ -1835,15 +1914,40 @@ class FieldTest(unittest.TestCase):
|
||||
|
||||
t = TestImage.objects.first()
|
||||
|
||||
self.assertEquals(t.image.format, 'PNG')
|
||||
self.assertEqual(t.image.format, 'PNG')
|
||||
w, h = t.image.size
|
||||
|
||||
self.assertEquals(w, 185)
|
||||
self.assertEquals(h, 37)
|
||||
self.assertEqual(w, 185)
|
||||
self.assertEqual(h, 37)
|
||||
|
||||
t.image.delete()
|
||||
|
||||
def test_image_field_resize_force(self):
|
||||
if PY3:
|
||||
raise SkipTest('PIL does not have Python 3 support')
|
||||
|
||||
class TestImage(Document):
|
||||
image = ImageField(size=(185, 37, True))
|
||||
|
||||
TestImage.drop_collection()
|
||||
|
||||
t = TestImage()
|
||||
t.image.put(open(TEST_IMAGE_PATH, 'r'))
|
||||
t.save()
|
||||
|
||||
t = TestImage.objects.first()
|
||||
|
||||
self.assertEqual(t.image.format, 'PNG')
|
||||
w, h = t.image.size
|
||||
|
||||
self.assertEqual(w, 185)
|
||||
self.assertEqual(h, 37)
|
||||
|
||||
t.image.delete()
|
||||
|
||||
def test_image_field_thumbnail(self):
|
||||
if PY3:
|
||||
raise SkipTest('PIL does not have Python 3 support')
|
||||
|
||||
class TestImage(Document):
|
||||
image = ImageField(thumbnail_size=(92, 18))
|
||||
@@ -1856,13 +1960,12 @@ class FieldTest(unittest.TestCase):
|
||||
|
||||
t = TestImage.objects.first()
|
||||
|
||||
self.assertEquals(t.image.thumbnail.format, 'PNG')
|
||||
self.assertEquals(t.image.thumbnail.width, 92)
|
||||
self.assertEquals(t.image.thumbnail.height, 18)
|
||||
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')
|
||||
class TestFile(Document):
|
||||
@@ -1879,16 +1982,16 @@ class FieldTest(unittest.TestCase):
|
||||
# First instance
|
||||
test_file = TestFile()
|
||||
test_file.name = "Hello, World!"
|
||||
test_file.the_file.put('Hello, World!',
|
||||
test_file.the_file.put(b('Hello, World!'),
|
||||
name="hello.txt")
|
||||
test_file.save()
|
||||
|
||||
data = get_db("test_files").macumba.files.find_one()
|
||||
self.assertEquals(data.get('name'), 'hello.txt')
|
||||
self.assertEqual(data.get('name'), 'hello.txt')
|
||||
|
||||
test_file = TestFile.objects.first()
|
||||
self.assertEquals(test_file.the_file.read(),
|
||||
'Hello, World!')
|
||||
self.assertEqual(test_file.the_file.read(),
|
||||
b('Hello, World!'))
|
||||
|
||||
def test_geo_indexes(self):
|
||||
"""Ensure that indexes are created automatically for GeoPointFields.
|
||||
@@ -1963,6 +2066,27 @@ class FieldTest(unittest.TestCase):
|
||||
c = self.db['mongoengine.counters'].find_one({'_id': 'person.id'})
|
||||
self.assertEqual(c['next'], 10)
|
||||
|
||||
def test_sequence_field_sequence_name(self):
|
||||
class Person(Document):
|
||||
id = SequenceField(primary_key=True, sequence_name='jelly')
|
||||
name = StringField()
|
||||
|
||||
self.db['mongoengine.counters'].drop()
|
||||
Person.drop_collection()
|
||||
|
||||
for x in xrange(10):
|
||||
p = Person(name="Person %s" % x)
|
||||
p.save()
|
||||
|
||||
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)
|
||||
|
||||
def test_multiple_sequence_fields(self):
|
||||
class Person(Document):
|
||||
id = SequenceField(primary_key=True)
|
||||
@@ -2149,27 +2273,27 @@ class FieldTest(unittest.TestCase):
|
||||
post.comments.append(Comment(content='hello', author=bob))
|
||||
post.comments.append(Comment(author=bob))
|
||||
|
||||
self.assertRaises(ValidationError, post.validate)
|
||||
try:
|
||||
post.validate()
|
||||
except ValidationError, error:
|
||||
pass
|
||||
# ValidationError.errors property
|
||||
self.assertTrue(hasattr(error, 'errors'))
|
||||
self.assertTrue(isinstance(error.errors, dict))
|
||||
self.assertTrue('comments' in error.errors)
|
||||
self.assertTrue(1 in error.errors['comments'])
|
||||
self.assertTrue(isinstance(error.errors['comments'][1]['content'],
|
||||
ValidationError))
|
||||
|
||||
# ValidationError.errors property
|
||||
self.assertTrue(hasattr(error, 'errors'))
|
||||
self.assertTrue(isinstance(error.errors, dict))
|
||||
self.assertTrue('comments' in error.errors)
|
||||
self.assertTrue(1 in error.errors['comments'])
|
||||
self.assertTrue(isinstance(error.errors['comments'][1]['content'],
|
||||
ValidationError))
|
||||
# ValidationError.schema property
|
||||
error_dict = error.to_dict()
|
||||
self.assertTrue(isinstance(error_dict, dict))
|
||||
self.assertTrue('comments' in error_dict)
|
||||
self.assertTrue(1 in error_dict['comments'])
|
||||
self.assertTrue('content' in error_dict['comments'][1])
|
||||
self.assertEqual(error_dict['comments'][1]['content'],
|
||||
u'Field is required')
|
||||
|
||||
# ValidationError.schema property
|
||||
error_dict = error.to_dict()
|
||||
self.assertTrue(isinstance(error_dict, dict))
|
||||
self.assertTrue('comments' in error_dict)
|
||||
self.assertTrue(1 in error_dict['comments'])
|
||||
self.assertTrue('content' in error_dict['comments'][1])
|
||||
self.assertEquals(error_dict['comments'][1]['content'],
|
||||
'Field is required')
|
||||
|
||||
post.comments[1].content = 'here we go'
|
||||
post.validate()
|
||||
|
@@ -1,16 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import with_statement
|
||||
import unittest
|
||||
import pymongo
|
||||
from bson import ObjectId
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import pymongo
|
||||
|
||||
from bson import ObjectId
|
||||
|
||||
from mongoengine import *
|
||||
from mongoengine.connection import get_connection
|
||||
from mongoengine.python_support import PY3
|
||||
from mongoengine.tests import query_counter
|
||||
from mongoengine.queryset import (QuerySet, QuerySetManager,
|
||||
MultipleObjectsReturned, DoesNotExist,
|
||||
QueryFieldList)
|
||||
from mongoengine import *
|
||||
from mongoengine.connection import get_connection
|
||||
from mongoengine.tests import query_counter
|
||||
|
||||
|
||||
class QuerySetTest(unittest.TestCase):
|
||||
|
||||
@@ -21,6 +24,8 @@ class QuerySetTest(unittest.TestCase):
|
||||
name = StringField()
|
||||
age = IntField()
|
||||
meta = {'allow_inheritance': True}
|
||||
|
||||
Person.drop_collection()
|
||||
self.Person = Person
|
||||
|
||||
def test_initialisation(self):
|
||||
@@ -239,11 +244,11 @@ class QuerySetTest(unittest.TestCase):
|
||||
self.Person.objects.update(set__name='Ross', write_options=write_options)
|
||||
|
||||
author = self.Person.objects.first()
|
||||
self.assertEquals(author.name, 'Ross')
|
||||
self.assertEqual(author.name, 'Ross')
|
||||
|
||||
self.Person.objects.update_one(set__name='Test User', write_options=write_options)
|
||||
author = self.Person.objects.first()
|
||||
self.assertEquals(author.name, 'Test User')
|
||||
self.assertEqual(author.name, 'Test User')
|
||||
|
||||
def test_update_update_has_a_value(self):
|
||||
"""Test to ensure that update is passed a value to update to"""
|
||||
@@ -332,8 +337,8 @@ class QuerySetTest(unittest.TestCase):
|
||||
BlogPost.objects(comments__by="jane").update(inc__comments__S__votes=1)
|
||||
|
||||
post = BlogPost.objects.first()
|
||||
self.assertEquals(post.comments[1].by, 'jane')
|
||||
self.assertEquals(post.comments[1].votes, 8)
|
||||
self.assertEqual(post.comments[1].by, 'jane')
|
||||
self.assertEqual(post.comments[1].votes, 8)
|
||||
|
||||
# Currently the $ operator only applies to the first matched item in
|
||||
# the query
|
||||
@@ -346,7 +351,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
Simple.objects(x=2).update(inc__x__S=1)
|
||||
|
||||
simple = Simple.objects.first()
|
||||
self.assertEquals(simple.x, [1, 3, 3, 2])
|
||||
self.assertEqual(simple.x, [1, 3, 3, 2])
|
||||
Simple.drop_collection()
|
||||
|
||||
# You can set multiples
|
||||
@@ -358,10 +363,10 @@ class QuerySetTest(unittest.TestCase):
|
||||
Simple.objects(x=3).update(set__x__S=0)
|
||||
|
||||
s = Simple.objects()
|
||||
self.assertEquals(s[0].x, [1, 2, 0, 4])
|
||||
self.assertEquals(s[1].x, [2, 0, 4, 5])
|
||||
self.assertEquals(s[2].x, [0, 4, 5, 6])
|
||||
self.assertEquals(s[3].x, [4, 5, 6, 7])
|
||||
self.assertEqual(s[0].x, [1, 2, 0, 4])
|
||||
self.assertEqual(s[1].x, [2, 0, 4, 5])
|
||||
self.assertEqual(s[2].x, [0, 4, 5, 6])
|
||||
self.assertEqual(s[3].x, [4, 5, 6, 7])
|
||||
|
||||
# Using "$unset" with an expression like this "array.$" will result in
|
||||
# the array item becoming None, not being removed.
|
||||
@@ -369,14 +374,14 @@ class QuerySetTest(unittest.TestCase):
|
||||
Simple(x=[1, 2, 3, 4, 3, 2, 3, 4]).save()
|
||||
Simple.objects(x=3).update(unset__x__S=1)
|
||||
simple = Simple.objects.first()
|
||||
self.assertEquals(simple.x, [1, 2, None, 4, 3, 2, 3, 4])
|
||||
self.assertEqual(simple.x, [1, 2, None, 4, 3, 2, 3, 4])
|
||||
|
||||
# Nested updates arent supported yet..
|
||||
def update_nested():
|
||||
Simple.drop_collection()
|
||||
Simple(x=[{'test': [1, 2, 3, 4]}]).save()
|
||||
Simple.objects(x__test=2).update(set__x__S__test__S=3)
|
||||
self.assertEquals(simple.x, [1, 2, 3, 4])
|
||||
self.assertEqual(simple.x, [1, 2, 3, 4])
|
||||
|
||||
self.assertRaises(OperationError, update_nested)
|
||||
Simple.drop_collection()
|
||||
@@ -406,8 +411,8 @@ class QuerySetTest(unittest.TestCase):
|
||||
BlogPost.objects(comments__by="joe").update(set__comments__S__votes=Vote(score=4))
|
||||
|
||||
post = BlogPost.objects.first()
|
||||
self.assertEquals(post.comments[0].by, 'joe')
|
||||
self.assertEquals(post.comments[0].votes.score, 4)
|
||||
self.assertEqual(post.comments[0].by, 'joe')
|
||||
self.assertEqual(post.comments[0].votes.score, 4)
|
||||
|
||||
def test_mapfield_update(self):
|
||||
"""Ensure that the MapField can be updated."""
|
||||
@@ -496,7 +501,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
|
||||
Blog.drop_collection()
|
||||
|
||||
# Recreates the collection
|
||||
# Recreates the collection
|
||||
self.assertEqual(0, Blog.objects.count())
|
||||
|
||||
with query_counter() as q:
|
||||
@@ -561,7 +566,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
Blog.drop_collection()
|
||||
blog1 = Blog(title="code", posts=[post1, post2])
|
||||
obj_id = Blog.objects.insert(blog1, load_bulk=False)
|
||||
self.assertEquals(obj_id.__class__.__name__, 'ObjectId')
|
||||
self.assertEqual(obj_id.__class__.__name__, 'ObjectId')
|
||||
|
||||
Blog.drop_collection()
|
||||
post3 = Post(comments=[comment1, comment1])
|
||||
@@ -573,7 +578,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
def throw_operation_error_not_unique():
|
||||
Blog.objects.insert([blog2, blog3], safe=True)
|
||||
|
||||
self.assertRaises(OperationError, throw_operation_error_not_unique)
|
||||
self.assertRaises(NotUniqueError, throw_operation_error_not_unique)
|
||||
self.assertEqual(Blog.objects.count(), 2)
|
||||
|
||||
Blog.objects.insert([blog2, blog3], write_options={'continue_on_error': True})
|
||||
@@ -619,7 +624,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
fresh_o1 = Organization.objects.get(id=o1.id)
|
||||
fresh_o1.save()
|
||||
|
||||
self.assertEquals(q, 2)
|
||||
self.assertEqual(q, 2)
|
||||
|
||||
with query_counter() as q:
|
||||
self.assertEqual(q, 0)
|
||||
@@ -627,7 +632,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
fresh_o1 = Organization.objects.get(id=o1.id)
|
||||
fresh_o1.save(cascade=False)
|
||||
|
||||
self.assertEquals(q, 2)
|
||||
self.assertEqual(q, 2)
|
||||
|
||||
with query_counter() as q:
|
||||
self.assertEqual(q, 0)
|
||||
@@ -636,7 +641,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
fresh_o1.employees.append(p2)
|
||||
fresh_o1.save(cascade=False)
|
||||
|
||||
self.assertEquals(q, 3)
|
||||
self.assertEqual(q, 3)
|
||||
|
||||
def test_slave_okay(self):
|
||||
"""Ensures that a query can take slave_okay syntax
|
||||
@@ -710,20 +715,20 @@ class QuerySetTest(unittest.TestCase):
|
||||
|
||||
docs = Doc.objects.order_by('number')
|
||||
|
||||
self.assertEquals(docs.count(), 1000)
|
||||
self.assertEquals(len(docs), 1000)
|
||||
self.assertEqual(docs.count(), 1000)
|
||||
self.assertEqual(len(docs), 1000)
|
||||
|
||||
docs_string = "%s" % docs
|
||||
self.assertTrue("Doc: 0" in docs_string)
|
||||
|
||||
self.assertEquals(docs.count(), 1000)
|
||||
self.assertEquals(len(docs), 1000)
|
||||
self.assertEqual(docs.count(), 1000)
|
||||
self.assertEqual(len(docs), 1000)
|
||||
|
||||
# Limit and skip
|
||||
self.assertEquals('[<Doc: 1>, <Doc: 2>, <Doc: 3>]', "%s" % docs[1:4])
|
||||
self.assertEqual('[<Doc: 1>, <Doc: 2>, <Doc: 3>]', "%s" % docs[1:4])
|
||||
|
||||
self.assertEquals(docs.count(), 3)
|
||||
self.assertEquals(len(docs), 3)
|
||||
self.assertEqual(docs.count(), 3)
|
||||
self.assertEqual(len(docs), 3)
|
||||
for doc in docs:
|
||||
self.assertEqual('.. queryset mid-iteration ..', repr(docs))
|
||||
|
||||
@@ -880,6 +885,21 @@ class QuerySetTest(unittest.TestCase):
|
||||
BlogPost.drop_collection()
|
||||
Blog.drop_collection()
|
||||
|
||||
def test_raw_and_merging(self):
|
||||
class Doc(Document):
|
||||
pass
|
||||
|
||||
raw_query = Doc.objects(__raw__={'deleted': False,
|
||||
'scraped': 'yes',
|
||||
'$nor': [{'views.extracted': 'no'},
|
||||
{'attachments.views.extracted':'no'}]
|
||||
})._query
|
||||
|
||||
expected = {'deleted': False, '_types': 'Doc', 'scraped': 'yes',
|
||||
'$nor': [{'views.extracted': 'no'},
|
||||
{'attachments.views.extracted': 'no'}]}
|
||||
self.assertEqual(expected, raw_query)
|
||||
|
||||
def test_ordering(self):
|
||||
"""Ensure default ordering is applied and can be overridden.
|
||||
"""
|
||||
@@ -1104,27 +1124,27 @@ class QuerySetTest(unittest.TestCase):
|
||||
|
||||
# first three
|
||||
numbers = Numbers.objects.fields(slice__n=3).get()
|
||||
self.assertEquals(numbers.n, [0, 1, 2])
|
||||
self.assertEqual(numbers.n, [0, 1, 2])
|
||||
|
||||
# last three
|
||||
numbers = Numbers.objects.fields(slice__n=-3).get()
|
||||
self.assertEquals(numbers.n, [-3, -2, -1])
|
||||
self.assertEqual(numbers.n, [-3, -2, -1])
|
||||
|
||||
# skip 2, limit 3
|
||||
numbers = Numbers.objects.fields(slice__n=[2, 3]).get()
|
||||
self.assertEquals(numbers.n, [2, 3, 4])
|
||||
self.assertEqual(numbers.n, [2, 3, 4])
|
||||
|
||||
# skip to fifth from last, limit 4
|
||||
numbers = Numbers.objects.fields(slice__n=[-5, 4]).get()
|
||||
self.assertEquals(numbers.n, [-5, -4, -3, -2])
|
||||
self.assertEqual(numbers.n, [-5, -4, -3, -2])
|
||||
|
||||
# skip to fifth from last, limit 10
|
||||
numbers = Numbers.objects.fields(slice__n=[-5, 10]).get()
|
||||
self.assertEquals(numbers.n, [-5, -4, -3, -2, -1])
|
||||
self.assertEqual(numbers.n, [-5, -4, -3, -2, -1])
|
||||
|
||||
# skip to fifth from last, limit 10 dict method
|
||||
numbers = Numbers.objects.fields(n={"$slice": [-5, 10]}).get()
|
||||
self.assertEquals(numbers.n, [-5, -4, -3, -2, -1])
|
||||
self.assertEqual(numbers.n, [-5, -4, -3, -2, -1])
|
||||
|
||||
def test_slicing_nested_fields(self):
|
||||
"""Ensure that query slicing an embedded array works.
|
||||
@@ -1144,27 +1164,27 @@ class QuerySetTest(unittest.TestCase):
|
||||
|
||||
# first three
|
||||
numbers = Numbers.objects.fields(slice__embedded__n=3).get()
|
||||
self.assertEquals(numbers.embedded.n, [0, 1, 2])
|
||||
self.assertEqual(numbers.embedded.n, [0, 1, 2])
|
||||
|
||||
# last three
|
||||
numbers = Numbers.objects.fields(slice__embedded__n=-3).get()
|
||||
self.assertEquals(numbers.embedded.n, [-3, -2, -1])
|
||||
self.assertEqual(numbers.embedded.n, [-3, -2, -1])
|
||||
|
||||
# skip 2, limit 3
|
||||
numbers = Numbers.objects.fields(slice__embedded__n=[2, 3]).get()
|
||||
self.assertEquals(numbers.embedded.n, [2, 3, 4])
|
||||
self.assertEqual(numbers.embedded.n, [2, 3, 4])
|
||||
|
||||
# skip to fifth from last, limit 4
|
||||
numbers = Numbers.objects.fields(slice__embedded__n=[-5, 4]).get()
|
||||
self.assertEquals(numbers.embedded.n, [-5, -4, -3, -2])
|
||||
self.assertEqual(numbers.embedded.n, [-5, -4, -3, -2])
|
||||
|
||||
# skip to fifth from last, limit 10
|
||||
numbers = Numbers.objects.fields(slice__embedded__n=[-5, 10]).get()
|
||||
self.assertEquals(numbers.embedded.n, [-5, -4, -3, -2, -1])
|
||||
self.assertEqual(numbers.embedded.n, [-5, -4, -3, -2, -1])
|
||||
|
||||
# skip to fifth from last, limit 10 dict method
|
||||
numbers = Numbers.objects.fields(embedded__n={"$slice": [-5, 10]}).get()
|
||||
self.assertEquals(numbers.embedded.n, [-5, -4, -3, -2, -1])
|
||||
self.assertEqual(numbers.embedded.n, [-5, -4, -3, -2, -1])
|
||||
|
||||
def test_find_embedded(self):
|
||||
"""Ensure that an embedded document is properly returned from a query.
|
||||
@@ -1248,7 +1268,6 @@ class QuerySetTest(unittest.TestCase):
|
||||
published_posts = (post1, post2, post3, post5, post6)
|
||||
self.assertTrue(all(obj.id in posts for obj in published_posts))
|
||||
|
||||
|
||||
# Check Q object combination
|
||||
date = datetime(2010, 1, 10)
|
||||
q = BlogPost.objects(Q(publish_date__lte=date) | Q(published=True))
|
||||
@@ -1307,6 +1326,27 @@ class QuerySetTest(unittest.TestCase):
|
||||
|
||||
BlogPost.drop_collection()
|
||||
|
||||
def test_raw_query_and_Q_objects(self):
|
||||
"""
|
||||
Test raw plays nicely
|
||||
"""
|
||||
class Foo(Document):
|
||||
name = StringField()
|
||||
a = StringField()
|
||||
b = StringField()
|
||||
c = StringField()
|
||||
|
||||
meta = {
|
||||
'allow_inheritance': False
|
||||
}
|
||||
|
||||
query = Foo.objects(__raw__={'$nor': [{'name': 'bar'}]})._query
|
||||
self.assertEqual(query, {'$nor': [{'name': 'bar'}]})
|
||||
|
||||
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})
|
||||
|
||||
def test_exec_js_query(self):
|
||||
"""Ensure that queries are properly formed for use in exec_js.
|
||||
"""
|
||||
@@ -1405,7 +1445,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
# Test template style
|
||||
code = "{{~comments.content}}"
|
||||
sub_code = BlogPost.objects._sub_js_fields(code)
|
||||
self.assertEquals("cmnts.body", sub_code)
|
||||
self.assertEqual("cmnts.body", sub_code)
|
||||
|
||||
BlogPost.drop_collection()
|
||||
|
||||
@@ -1452,6 +1492,8 @@ class QuerySetTest(unittest.TestCase):
|
||||
name = StringField()
|
||||
parent = ReferenceField('self', reverse_delete_rule=CASCADE)
|
||||
|
||||
Category.drop_collection()
|
||||
|
||||
num_children = 3
|
||||
base = Category(name='Root')
|
||||
base.save()
|
||||
@@ -1468,13 +1510,13 @@ class QuerySetTest(unittest.TestCase):
|
||||
child_child.save()
|
||||
|
||||
tree_size = 1 + num_children + (num_children * num_children)
|
||||
self.assertEquals(tree_size, Category.objects.count())
|
||||
self.assertEquals(num_children, Category.objects(parent=base).count())
|
||||
self.assertEqual(tree_size, Category.objects.count())
|
||||
self.assertEqual(num_children, Category.objects(parent=base).count())
|
||||
|
||||
# The delete should effectively wipe out the Category collection
|
||||
# without resulting in infinite parent-child cascade recursion
|
||||
base.delete()
|
||||
self.assertEquals(0, Category.objects.count())
|
||||
self.assertEqual(0, Category.objects.count())
|
||||
|
||||
def test_reverse_delete_rule_nullify(self):
|
||||
"""Ensure nullification of references to deleted documents.
|
||||
@@ -1735,7 +1777,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
|
||||
BlogPost.objects(slug="test-2").update_one(set__tags__0__name="python")
|
||||
post.reload()
|
||||
self.assertEquals(post.tags[0].name, 'python')
|
||||
self.assertEqual(post.tags[0].name, 'python')
|
||||
|
||||
BlogPost.objects(slug="test-2").update_one(pop__tags=-1)
|
||||
post.reload()
|
||||
@@ -1762,7 +1804,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
set__authors__S=Author(name="Ross"))
|
||||
|
||||
message = message.reload()
|
||||
self.assertEquals(message.authors[0].name, "Ross")
|
||||
self.assertEqual(message.authors[0].name, "Ross")
|
||||
|
||||
Message.objects(authors__name="Ross").update_one(
|
||||
set__authors=[Author(name="Harry"),
|
||||
@@ -1770,9 +1812,9 @@ class QuerySetTest(unittest.TestCase):
|
||||
Author(name="Adam")])
|
||||
|
||||
message = message.reload()
|
||||
self.assertEquals(message.authors[0].name, "Harry")
|
||||
self.assertEquals(message.authors[1].name, "Ross")
|
||||
self.assertEquals(message.authors[2].name, "Adam")
|
||||
self.assertEqual(message.authors[0].name, "Harry")
|
||||
self.assertEqual(message.authors[1].name, "Ross")
|
||||
self.assertEqual(message.authors[2].name, "Adam")
|
||||
|
||||
def test_order_by(self):
|
||||
"""Ensure that QuerySets may be ordered.
|
||||
@@ -1852,10 +1894,10 @@ class QuerySetTest(unittest.TestCase):
|
||||
results = list(results)
|
||||
self.assertEqual(len(results), 4)
|
||||
|
||||
music = filter(lambda r: r.key == "music", results)[0]
|
||||
music = list(filter(lambda r: r.key == "music", results))[0]
|
||||
self.assertEqual(music.value, 2)
|
||||
|
||||
film = filter(lambda r: r.key == "film", results)[0]
|
||||
film = list(filter(lambda r: r.key == "film", results))[0]
|
||||
self.assertEqual(film.value, 3)
|
||||
|
||||
BlogPost.drop_collection()
|
||||
@@ -2155,15 +2197,15 @@ class QuerySetTest(unittest.TestCase):
|
||||
Person(name="Wilson Jr").save()
|
||||
|
||||
freq = Person.objects.item_frequencies('city')
|
||||
self.assertEquals(freq, {'CRB': 1.0, None: 1.0})
|
||||
self.assertEqual(freq, {'CRB': 1.0, None: 1.0})
|
||||
freq = Person.objects.item_frequencies('city', normalize=True)
|
||||
self.assertEquals(freq, {'CRB': 0.5, None: 0.5})
|
||||
self.assertEqual(freq, {'CRB': 0.5, None: 0.5})
|
||||
|
||||
|
||||
freq = Person.objects.item_frequencies('city', map_reduce=True)
|
||||
self.assertEquals(freq, {'CRB': 1.0, None: 1.0})
|
||||
self.assertEqual(freq, {'CRB': 1.0, None: 1.0})
|
||||
freq = Person.objects.item_frequencies('city', normalize=True, map_reduce=True)
|
||||
self.assertEquals(freq, {'CRB': 0.5, None: 0.5})
|
||||
self.assertEqual(freq, {'CRB': 0.5, None: 0.5})
|
||||
|
||||
def test_item_frequencies_with_null_embedded(self):
|
||||
class Data(EmbeddedDocument):
|
||||
@@ -2188,10 +2230,10 @@ class QuerySetTest(unittest.TestCase):
|
||||
p.save()
|
||||
|
||||
ot = Person.objects.item_frequencies('extra.tag', map_reduce=False)
|
||||
self.assertEquals(ot, {None: 1.0, u'friend': 1.0})
|
||||
self.assertEqual(ot, {None: 1.0, u'friend': 1.0})
|
||||
|
||||
ot = Person.objects.item_frequencies('extra.tag', map_reduce=True)
|
||||
self.assertEquals(ot, {None: 1.0, u'friend': 1.0})
|
||||
self.assertEqual(ot, {None: 1.0, u'friend': 1.0})
|
||||
|
||||
def test_item_frequencies_with_0_values(self):
|
||||
class Test(Document):
|
||||
@@ -2203,9 +2245,9 @@ class QuerySetTest(unittest.TestCase):
|
||||
t.save()
|
||||
|
||||
ot = Test.objects.item_frequencies('val', map_reduce=True)
|
||||
self.assertEquals(ot, {0: 1})
|
||||
self.assertEqual(ot, {0: 1})
|
||||
ot = Test.objects.item_frequencies('val', map_reduce=False)
|
||||
self.assertEquals(ot, {0: 1})
|
||||
self.assertEqual(ot, {0: 1})
|
||||
|
||||
def test_item_frequencies_with_False_values(self):
|
||||
class Test(Document):
|
||||
@@ -2217,9 +2259,9 @@ class QuerySetTest(unittest.TestCase):
|
||||
t.save()
|
||||
|
||||
ot = Test.objects.item_frequencies('val', map_reduce=True)
|
||||
self.assertEquals(ot, {False: 1})
|
||||
self.assertEqual(ot, {False: 1})
|
||||
ot = Test.objects.item_frequencies('val', map_reduce=False)
|
||||
self.assertEquals(ot, {False: 1})
|
||||
self.assertEqual(ot, {False: 1})
|
||||
|
||||
def test_item_frequencies_normalize(self):
|
||||
class Test(Document):
|
||||
@@ -2234,10 +2276,10 @@ class QuerySetTest(unittest.TestCase):
|
||||
Test(val=2).save()
|
||||
|
||||
freqs = Test.objects.item_frequencies('val', map_reduce=False, normalize=True)
|
||||
self.assertEquals(freqs, {1: 50.0/70, 2: 20.0/70})
|
||||
self.assertEqual(freqs, {1: 50.0/70, 2: 20.0/70})
|
||||
|
||||
freqs = Test.objects.item_frequencies('val', map_reduce=True, normalize=True)
|
||||
self.assertEquals(freqs, {1: 50.0/70, 2: 20.0/70})
|
||||
self.assertEqual(freqs, {1: 50.0/70, 2: 20.0/70})
|
||||
|
||||
def test_average(self):
|
||||
"""Ensure that field can be averaged correctly.
|
||||
@@ -2297,7 +2339,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
foo = Foo(bar=bar)
|
||||
foo.save()
|
||||
|
||||
self.assertEquals(Foo.objects.distinct("bar"), [bar])
|
||||
self.assertEqual(Foo.objects.distinct("bar"), [bar])
|
||||
|
||||
def test_distinct_handles_references_to_alias(self):
|
||||
register_connection('testdb', 'mongoenginetest2')
|
||||
@@ -2319,7 +2361,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
foo = Foo(bar=bar)
|
||||
foo.save()
|
||||
|
||||
self.assertEquals(Foo.objects.distinct("bar"), [bar])
|
||||
self.assertEqual(Foo.objects.distinct("bar"), [bar])
|
||||
|
||||
def test_custom_manager(self):
|
||||
"""Ensure that custom QuerySetManager instances work as expected.
|
||||
@@ -2502,6 +2544,24 @@ class QuerySetTest(unittest.TestCase):
|
||||
|
||||
BlogPost.drop_collection()
|
||||
|
||||
def test_types_index_with_pk(self):
|
||||
|
||||
class Comment(EmbeddedDocument):
|
||||
comment_id = IntField(required=True)
|
||||
|
||||
try:
|
||||
class BlogPost(Document):
|
||||
comments = EmbeddedDocumentField(Comment)
|
||||
meta = {'indexes': [{'fields': ['pk', 'comments.comment_id'],
|
||||
'unique': True}]}
|
||||
except UnboundLocalError:
|
||||
self.fail('Unbound local error at types index + pk definition')
|
||||
|
||||
info = BlogPost.objects._collection.index_information()
|
||||
info = [value['key'] for key, value in info.iteritems()]
|
||||
index_item = [(u'_types', 1), (u'_id', 1), (u'comments.comment_id', 1)]
|
||||
self.assertTrue(index_item in info)
|
||||
|
||||
def test_dict_with_custom_baseclass(self):
|
||||
"""Ensure DictField working with custom base clases.
|
||||
"""
|
||||
@@ -2779,8 +2839,8 @@ class QuerySetTest(unittest.TestCase):
|
||||
|
||||
Post().save()
|
||||
Post(is_published=True).save()
|
||||
self.assertEquals(Post.objects.count(), 2)
|
||||
self.assertEquals(Post.published.count(), 1)
|
||||
self.assertEqual(Post.objects.count(), 2)
|
||||
self.assertEqual(Post.published.count(), 1)
|
||||
|
||||
Post.drop_collection()
|
||||
|
||||
@@ -2946,10 +3006,10 @@ class QuerySetTest(unittest.TestCase):
|
||||
Number(n=3).save()
|
||||
|
||||
numbers = [n.n for n in Number.objects.order_by('-n')]
|
||||
self.assertEquals([3, 2, 1], numbers)
|
||||
self.assertEqual([3, 2, 1], numbers)
|
||||
|
||||
numbers = [n.n for n in Number.objects.order_by('+n')]
|
||||
self.assertEquals([1, 2, 3], numbers)
|
||||
self.assertEqual([1, 2, 3], numbers)
|
||||
Number.drop_collection()
|
||||
|
||||
|
||||
@@ -3271,15 +3331,22 @@ class QuerySetTest(unittest.TestCase):
|
||||
self.assertEqual(len(self.Person.objects.scalar('name')), 55)
|
||||
self.assertEqual("A0", "%s" % self.Person.objects.order_by('name').scalar('name').first())
|
||||
self.assertEqual("A0", "%s" % self.Person.objects.scalar('name').order_by('name')[0])
|
||||
self.assertEqual("[u'A1', u'A2']", "%s" % self.Person.objects.order_by('age').scalar('name')[1:3])
|
||||
self.assertEqual("[u'A51', u'A52']", "%s" % self.Person.objects.order_by('age').scalar('name')[51:53])
|
||||
if PY3:
|
||||
self.assertEqual("['A1', 'A2']", "%s" % self.Person.objects.order_by('age').scalar('name')[1:3])
|
||||
self.assertEqual("['A51', 'A52']", "%s" % self.Person.objects.order_by('age').scalar('name')[51:53])
|
||||
else:
|
||||
self.assertEqual("[u'A1', u'A2']", "%s" % self.Person.objects.order_by('age').scalar('name')[1:3])
|
||||
self.assertEqual("[u'A51', u'A52']", "%s" % self.Person.objects.order_by('age').scalar('name')[51:53])
|
||||
|
||||
# with_id and in_bulk
|
||||
person = self.Person.objects.order_by('name').first()
|
||||
self.assertEqual("A0", "%s" % self.Person.objects.scalar('name').with_id(person.id))
|
||||
|
||||
pks = self.Person.objects.order_by('age').scalar('pk')[1:3]
|
||||
self.assertEqual("[u'A1', u'A2']", "%s" % sorted(self.Person.objects.scalar('name').in_bulk(list(pks)).values()))
|
||||
if PY3:
|
||||
self.assertEqual("['A1', 'A2']", "%s" % sorted(self.Person.objects.scalar('name').in_bulk(list(pks)).values()))
|
||||
else:
|
||||
self.assertEqual("[u'A1', u'A2']", "%s" % sorted(self.Person.objects.scalar('name').in_bulk(list(pks)).values()))
|
||||
|
||||
|
||||
class QTest(unittest.TestCase):
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import unittest
|
||||
|
||||
import pymongo
|
||||
from pymongo import ReadPreference, ReplicaSetConnection
|
||||
|
||||
@@ -26,7 +27,7 @@ class ConnectionTest(unittest.TestCase):
|
||||
if not isinstance(conn, ReplicaSetConnection):
|
||||
return
|
||||
|
||||
self.assertEquals(conn.read_preference, ReadPreference.SECONDARY_ONLY)
|
||||
self.assertEqual(conn.read_preference, ReadPreference.SECONDARY_ONLY)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
@@ -212,9 +212,9 @@ class SignalTests(unittest.TestCase):
|
||||
|
||||
# The output of this signal is not entirely deterministic. The reloaded
|
||||
# object will have an object ID. Hence, we only check part of the output
|
||||
self.assertEquals(signal_output[3],
|
||||
self.assertEqual(signal_output[3],
|
||||
"pre_bulk_insert signal, [<Author: Bill Shakespeare>]")
|
||||
self.assertEquals(signal_output[-2:],
|
||||
self.assertEqual(signal_output[-2:],
|
||||
["post_bulk_insert signal, [<Author: Bill Shakespeare>]",
|
||||
"Is loaded",])
|
||||
|
||||
|
Reference in New Issue
Block a user