Updated for pymongo
This commit is contained in:
parent
69989365c7
commit
3da37fbf6e
@ -12,7 +12,7 @@ __all__ = (document.__all__ + fields.__all__ + connection.__all__ +
|
||||
|
||||
__author__ = 'Harry Marr'
|
||||
|
||||
VERSION = (0, 4, 0)
|
||||
VERSION = (0, 4, 1)
|
||||
|
||||
def get_version():
|
||||
version = '%s.%s' % (VERSION[0], VERSION[1])
|
||||
|
@ -2,8 +2,8 @@ from queryset import QuerySet, QuerySetManager
|
||||
from queryset import DoesNotExist, MultipleObjectsReturned
|
||||
|
||||
import sys
|
||||
import bson
|
||||
import pymongo
|
||||
import pymongo.objectid
|
||||
|
||||
|
||||
_document_registry = {}
|
||||
@ -21,11 +21,11 @@ class BaseField(object):
|
||||
may be added to subclasses of `Document` to define a document's schema.
|
||||
"""
|
||||
|
||||
# Fields may have _types inserted into indexes by default
|
||||
# Fields may have _types inserted into indexes by default
|
||||
_index_with_types = True
|
||||
_geo_index = False
|
||||
|
||||
def __init__(self, db_field=None, name=None, required=False, default=None,
|
||||
def __init__(self, db_field=None, name=None, required=False, default=None,
|
||||
unique=False, unique_with=None, primary_key=False,
|
||||
validation=None, choices=None):
|
||||
self.db_field = (db_field or name) if not primary_key else '_id'
|
||||
@ -43,7 +43,7 @@ class BaseField(object):
|
||||
self.choices = choices
|
||||
|
||||
def __get__(self, instance, owner):
|
||||
"""Descriptor for retrieving a value from a field in a document. Do
|
||||
"""Descriptor for retrieving a value from a field in a document. Do
|
||||
any necessary conversion between Python and MongoDB types.
|
||||
"""
|
||||
if instance is None:
|
||||
@ -111,9 +111,9 @@ class ObjectIdField(BaseField):
|
||||
# return unicode(value)
|
||||
|
||||
def to_mongo(self, value):
|
||||
if not isinstance(value, pymongo.objectid.ObjectId):
|
||||
if not isinstance(value, bson.objectid.ObjectId):
|
||||
try:
|
||||
return pymongo.objectid.ObjectId(unicode(value))
|
||||
return bson.objectid.ObjectId(unicode(value))
|
||||
except Exception, e:
|
||||
#e.message attribute has been deprecated since Python 2.6
|
||||
raise ValidationError(unicode(e))
|
||||
@ -124,7 +124,7 @@ class ObjectIdField(BaseField):
|
||||
|
||||
def validate(self, value):
|
||||
try:
|
||||
pymongo.objectid.ObjectId(unicode(value))
|
||||
bson.objectid.ObjectId(unicode(value))
|
||||
except:
|
||||
raise ValidationError('Invalid Object ID')
|
||||
|
||||
@ -153,8 +153,8 @@ class DocumentMetaclass(type):
|
||||
superclasses.update(base._superclasses)
|
||||
|
||||
if hasattr(base, '_meta'):
|
||||
# Ensure that the Document class may be subclassed -
|
||||
# inheritance may be disabled to remove dependency on
|
||||
# Ensure that the Document class may be subclassed -
|
||||
# inheritance may be disabled to remove dependency on
|
||||
# additional fields _cls and _types
|
||||
if base._meta.get('allow_inheritance', True) == False:
|
||||
raise ValueError('Document %s may not be subclassed' %
|
||||
@ -193,12 +193,12 @@ class DocumentMetaclass(type):
|
||||
|
||||
module = attrs.get('__module__')
|
||||
|
||||
base_excs = tuple(base.DoesNotExist for base in bases
|
||||
base_excs = tuple(base.DoesNotExist for base in bases
|
||||
if hasattr(base, 'DoesNotExist')) or (DoesNotExist,)
|
||||
exc = subclass_exception('DoesNotExist', base_excs, module)
|
||||
new_class.add_to_class('DoesNotExist', exc)
|
||||
|
||||
base_excs = tuple(base.MultipleObjectsReturned for base in bases
|
||||
base_excs = tuple(base.MultipleObjectsReturned for base in bases
|
||||
if hasattr(base, 'MultipleObjectsReturned'))
|
||||
base_excs = base_excs or (MultipleObjectsReturned,)
|
||||
exc = subclass_exception('MultipleObjectsReturned', base_excs, module)
|
||||
@ -220,9 +220,9 @@ class TopLevelDocumentMetaclass(DocumentMetaclass):
|
||||
|
||||
def __new__(cls, name, bases, attrs):
|
||||
super_new = super(TopLevelDocumentMetaclass, cls).__new__
|
||||
# Classes defined in this package are abstract and should not have
|
||||
# Classes defined in this package are abstract and should not have
|
||||
# their own metadata with DB collection, etc.
|
||||
# __metaclass__ is only set on the class with the __metaclass__
|
||||
# __metaclass__ is only set on the class with the __metaclass__
|
||||
# attribute (i.e. it is not set on subclasses). This differentiates
|
||||
# 'real' documents from the 'Document' class
|
||||
if attrs.get('__metaclass__') == TopLevelDocumentMetaclass:
|
||||
@ -347,7 +347,7 @@ class BaseDocument(object):
|
||||
are present.
|
||||
"""
|
||||
# Get a list of tuples of field names and their current values
|
||||
fields = [(field, getattr(self, name))
|
||||
fields = [(field, getattr(self, name))
|
||||
for name, field in self._fields.items()]
|
||||
|
||||
# Ensure that each field is matched to a valid value
|
||||
|
@ -40,16 +40,16 @@ class Document(BaseDocument):
|
||||
presence of `_cls` and `_types`, set :attr:`allow_inheritance` to
|
||||
``False`` in the :attr:`meta` dictionary.
|
||||
|
||||
A :class:`~mongoengine.Document` may use a **Capped Collection** by
|
||||
A :class:`~mongoengine.Document` may use a **Capped Collection** by
|
||||
specifying :attr:`max_documents` and :attr:`max_size` in the :attr:`meta`
|
||||
dictionary. :attr:`max_documents` is the maximum number of documents that
|
||||
is allowed to be stored in the collection, and :attr:`max_size` is the
|
||||
maximum size of the collection in bytes. If :attr:`max_size` is not
|
||||
specified and :attr:`max_documents` is, :attr:`max_size` defaults to
|
||||
is allowed to be stored in the collection, and :attr:`max_size` is the
|
||||
maximum size of the collection in bytes. If :attr:`max_size` is not
|
||||
specified and :attr:`max_documents` is, :attr:`max_size` defaults to
|
||||
10000000 bytes (10MB).
|
||||
|
||||
Indexes may be created by specifying :attr:`indexes` in the :attr:`meta`
|
||||
dictionary. The value should be a list of field names or tuples of field
|
||||
dictionary. The value should be a list of field names or tuples of field
|
||||
names. Index direction may be specified by prefixing the field names with
|
||||
a **+** or **-** sign.
|
||||
"""
|
||||
@ -61,11 +61,11 @@ class Document(BaseDocument):
|
||||
document already exists, it will be updated, otherwise it will be
|
||||
created.
|
||||
|
||||
If ``safe=True`` and the operation is unsuccessful, an
|
||||
If ``safe=True`` and the operation is unsuccessful, an
|
||||
:class:`~mongoengine.OperationError` will be raised.
|
||||
|
||||
:param safe: check if the operation succeeded before returning
|
||||
:param force_insert: only try to create a new document, don't allow
|
||||
:param force_insert: only try to create a new document, don't allow
|
||||
updates of existing documents
|
||||
:param validate: validates the document; set to ``False`` for skiping
|
||||
"""
|
||||
@ -123,9 +123,9 @@ class MapReduceDocument(object):
|
||||
"""A document returned from a map/reduce query.
|
||||
|
||||
:param collection: An instance of :class:`~pymongo.Collection`
|
||||
:param key: Document/result key, often an instance of
|
||||
:class:`~pymongo.objectid.ObjectId`. If supplied as
|
||||
an ``ObjectId`` found in the given ``collection``,
|
||||
:param key: Document/result key, often an instance of
|
||||
:class:`~bson.objectid.ObjectId`. If supplied as
|
||||
an ``ObjectId`` found in the given ``collection``,
|
||||
the object can be accessed via the ``object`` property.
|
||||
:param value: The result(s) for this key.
|
||||
|
||||
@ -140,7 +140,7 @@ class MapReduceDocument(object):
|
||||
|
||||
@property
|
||||
def object(self):
|
||||
"""Lazy-load the object referenced by ``self.key``. ``self.key``
|
||||
"""Lazy-load the object referenced by ``self.key``. ``self.key``
|
||||
should be the ``primary_key``.
|
||||
"""
|
||||
id_field = self._document()._meta['id_field']
|
||||
|
@ -5,9 +5,9 @@ from operator import itemgetter
|
||||
|
||||
import re
|
||||
import pymongo
|
||||
import pymongo.dbref
|
||||
import pymongo.son
|
||||
import pymongo.binary
|
||||
import bson.dbref
|
||||
import bson.son
|
||||
import bson.binary
|
||||
import datetime
|
||||
import decimal
|
||||
import gridfs
|
||||
@ -300,13 +300,13 @@ class ListField(BaseField):
|
||||
|
||||
if isinstance(self.field, ReferenceField):
|
||||
referenced_type = self.field.document_type
|
||||
# Get value from document instance if available
|
||||
# Get value from document instance if available
|
||||
value_list = instance._data.get(self.name)
|
||||
if value_list:
|
||||
deref_list = []
|
||||
for value in value_list:
|
||||
# Dereference DBRefs
|
||||
if isinstance(value, (pymongo.dbref.DBRef)):
|
||||
if isinstance(value, (bson.dbref.DBRef)):
|
||||
value = _get_db().dereference(value)
|
||||
deref_list.append(referenced_type._from_son(value))
|
||||
else:
|
||||
@ -319,7 +319,7 @@ class ListField(BaseField):
|
||||
deref_list = []
|
||||
for value in value_list:
|
||||
# Dereference DBRefs
|
||||
if isinstance(value, (dict, pymongo.son.SON)):
|
||||
if isinstance(value, (dict, bson.son.SON)):
|
||||
deref_list.append(self.field.dereference(value))
|
||||
else:
|
||||
deref_list.append(value)
|
||||
@ -444,7 +444,7 @@ class ReferenceField(BaseField):
|
||||
# Get value from document instance if available
|
||||
value = instance._data.get(self.name)
|
||||
# Dereference DBRefs
|
||||
if isinstance(value, (pymongo.dbref.DBRef)):
|
||||
if isinstance(value, (bson.dbref.DBRef)):
|
||||
value = _get_db().dereference(value)
|
||||
if value is not None:
|
||||
instance._data[self.name] = self.document_type._from_son(value)
|
||||
@ -466,13 +466,13 @@ class ReferenceField(BaseField):
|
||||
|
||||
id_ = id_field.to_mongo(id_)
|
||||
collection = self.document_type._meta['collection']
|
||||
return pymongo.dbref.DBRef(collection, id_)
|
||||
return bson.dbref.DBRef(collection, id_)
|
||||
|
||||
def prepare_query_value(self, op, value):
|
||||
return self.to_mongo(value)
|
||||
|
||||
def validate(self, value):
|
||||
assert isinstance(value, (self.document_type, pymongo.dbref.DBRef))
|
||||
assert isinstance(value, (self.document_type, bson.dbref.DBRef))
|
||||
|
||||
def lookup_member(self, member_name):
|
||||
return self.document_type._fields.get(member_name)
|
||||
@ -490,7 +490,7 @@ class GenericReferenceField(BaseField):
|
||||
return self
|
||||
|
||||
value = instance._data.get(self.name)
|
||||
if isinstance(value, (dict, pymongo.son.SON)):
|
||||
if isinstance(value, (dict, bson.son.SON)):
|
||||
instance._data[self.name] = self.dereference(value)
|
||||
|
||||
return super(GenericReferenceField, self).__get__(instance, owner)
|
||||
@ -518,7 +518,7 @@ class GenericReferenceField(BaseField):
|
||||
|
||||
id_ = id_field.to_mongo(id_)
|
||||
collection = document._meta['collection']
|
||||
ref = pymongo.dbref.DBRef(collection, id_)
|
||||
ref = bson.dbref.DBRef(collection, id_)
|
||||
return {'_cls': document.__class__.__name__, '_ref': ref}
|
||||
|
||||
def prepare_query_value(self, op, value):
|
||||
@ -534,7 +534,7 @@ class BinaryField(BaseField):
|
||||
super(BinaryField, self).__init__(**kwargs)
|
||||
|
||||
def to_mongo(self, value):
|
||||
return pymongo.binary.Binary(value)
|
||||
return bson.binary.Binary(value)
|
||||
|
||||
def to_python(self, value):
|
||||
# Returns str not unicode as this is binary data
|
||||
@ -603,7 +603,7 @@ class GridFSProxy(object):
|
||||
if not self.newfile:
|
||||
self.new_file()
|
||||
self.grid_id = self.newfile._id
|
||||
self.newfile.writelines(lines)
|
||||
self.newfile.writelines(lines)
|
||||
|
||||
def read(self):
|
||||
try:
|
||||
@ -680,7 +680,7 @@ class FileField(BaseField):
|
||||
def validate(self, value):
|
||||
if value.grid_id is not None:
|
||||
assert isinstance(value, GridFSProxy)
|
||||
assert isinstance(value.grid_id, pymongo.objectid.ObjectId)
|
||||
assert isinstance(value.grid_id, bson.objectid.ObjectId)
|
||||
|
||||
|
||||
class GeoPointField(BaseField):
|
||||
|
@ -2,9 +2,9 @@ from connection import _get_db
|
||||
|
||||
import pprint
|
||||
import pymongo
|
||||
import pymongo.code
|
||||
import pymongo.dbref
|
||||
import pymongo.objectid
|
||||
import bson.code
|
||||
import bson.dbref
|
||||
import bson.objectid
|
||||
import re
|
||||
import copy
|
||||
import itertools
|
||||
@ -424,7 +424,7 @@ class QuerySet(object):
|
||||
}
|
||||
if self._loaded_fields:
|
||||
cursor_args['fields'] = self._loaded_fields
|
||||
self._cursor_obj = self._collection.find(self._query,
|
||||
self._cursor_obj = self._collection.find(self._query,
|
||||
**cursor_args)
|
||||
# Apply where clauses to cursor
|
||||
if self._where_clause:
|
||||
@ -476,8 +476,8 @@ class QuerySet(object):
|
||||
operators = ['ne', 'gt', 'gte', 'lt', 'lte', 'in', 'nin', 'mod',
|
||||
'all', 'size', 'exists', 'not']
|
||||
geo_operators = ['within_distance', 'within_box', 'near']
|
||||
match_operators = ['contains', 'icontains', 'startswith',
|
||||
'istartswith', 'endswith', 'iendswith',
|
||||
match_operators = ['contains', 'icontains', 'startswith',
|
||||
'istartswith', 'endswith', 'iendswith',
|
||||
'exact', 'iexact']
|
||||
|
||||
mongo_query = {}
|
||||
@ -563,8 +563,8 @@ class QuerySet(object):
|
||||
% self._document._class_name)
|
||||
|
||||
def get_or_create(self, *q_objs, **query):
|
||||
"""Retrieve unique object or create, if it doesn't exist. Returns a tuple of
|
||||
``(object, created)``, where ``object`` is the retrieved or created object
|
||||
"""Retrieve unique object or create, if it doesn't exist. Returns a tuple of
|
||||
``(object, created)``, where ``object`` is the retrieved or created object
|
||||
and ``created`` is a boolean specifying whether a new object was created. Raises
|
||||
:class:`~mongoengine.queryset.MultipleObjectsReturned` or
|
||||
`DocumentName.MultipleObjectsReturned` if multiple results are found.
|
||||
@ -667,8 +667,8 @@ class QuerySet(object):
|
||||
def __len__(self):
|
||||
return self.count()
|
||||
|
||||
def map_reduce(self, map_f, reduce_f, finalize_f=None, limit=None,
|
||||
scope=None, keep_temp=False):
|
||||
def map_reduce(self, map_f, reduce_f, output, finalize_f=None, limit=None,
|
||||
scope=None):
|
||||
"""Perform a map/reduce query using the current query spec
|
||||
and ordering. While ``map_reduce`` respects ``QuerySet`` chaining,
|
||||
it must be the last call made, as it does not return a maleable
|
||||
@ -678,52 +678,61 @@ class QuerySet(object):
|
||||
and :meth:`~mongoengine.tests.QuerySetTest.test_map_advanced`
|
||||
tests in ``tests.queryset.QuerySetTest`` for usage examples.
|
||||
|
||||
:param map_f: map function, as :class:`~pymongo.code.Code` or string
|
||||
:param map_f: map function, as :class:`~bson.code.Code` or string
|
||||
:param reduce_f: reduce function, as
|
||||
:class:`~pymongo.code.Code` or string
|
||||
: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.
|
||||
:param limit: number of objects from current query to provide
|
||||
to map/reduce method
|
||||
:param keep_temp: keep temporary table (boolean, default ``True``)
|
||||
|
||||
Returns an iterator yielding
|
||||
:class:`~mongoengine.document.MapReduceDocument`.
|
||||
|
||||
.. note:: Map/Reduce requires server version **>= 1.1.1**. The PyMongo
|
||||
:meth:`~pymongo.collection.Collection.map_reduce` helper requires
|
||||
PyMongo version **>= 1.2**.
|
||||
.. note::
|
||||
|
||||
Map/Reduce changed in server version **>= 1.7.4**. The PyMongo
|
||||
:meth:`~pymongo.collection.Collection.map_reduce` helper requires
|
||||
PyMongo version **>= 1.11**.
|
||||
|
||||
.. versionchanged:: 0.5
|
||||
- removed ``keep_temp`` keyword argument, which was only relevant
|
||||
for MongoDB server versions older than 1.7.4
|
||||
|
||||
.. versionadded:: 0.3
|
||||
"""
|
||||
from document import MapReduceDocument
|
||||
|
||||
if not hasattr(self._collection, "map_reduce"):
|
||||
raise NotImplementedError("Requires MongoDB >= 1.1.1")
|
||||
raise NotImplementedError("Requires MongoDB >= 1.7.1")
|
||||
|
||||
map_f_scope = {}
|
||||
if isinstance(map_f, pymongo.code.Code):
|
||||
if isinstance(map_f, bson.code.Code):
|
||||
map_f_scope = map_f.scope
|
||||
map_f = unicode(map_f)
|
||||
map_f = pymongo.code.Code(self._sub_js_fields(map_f), map_f_scope)
|
||||
map_f = bson.code.Code(self._sub_js_fields(map_f), map_f_scope)
|
||||
|
||||
reduce_f_scope = {}
|
||||
if isinstance(reduce_f, pymongo.code.Code):
|
||||
if isinstance(reduce_f, bson.code.Code):
|
||||
reduce_f_scope = reduce_f.scope
|
||||
reduce_f = unicode(reduce_f)
|
||||
reduce_f_code = self._sub_js_fields(reduce_f)
|
||||
reduce_f = pymongo.code.Code(reduce_f_code, reduce_f_scope)
|
||||
reduce_f = bson.code.Code(reduce_f_code, reduce_f_scope)
|
||||
|
||||
mr_args = {'query': self._query, 'keeptemp': keep_temp}
|
||||
mr_args = {'query': self._query}
|
||||
|
||||
if finalize_f:
|
||||
finalize_f_scope = {}
|
||||
if isinstance(finalize_f, pymongo.code.Code):
|
||||
if isinstance(finalize_f, bson.code.Code):
|
||||
finalize_f_scope = finalize_f.scope
|
||||
finalize_f = unicode(finalize_f)
|
||||
finalize_f_code = self._sub_js_fields(finalize_f)
|
||||
finalize_f = pymongo.code.Code(finalize_f_code, finalize_f_scope)
|
||||
finalize_f = bson.code.Code(finalize_f_code, finalize_f_scope)
|
||||
mr_args['finalize'] = finalize_f
|
||||
|
||||
if scope:
|
||||
@ -732,8 +741,16 @@ class QuerySet(object):
|
||||
if limit:
|
||||
mr_args['limit'] = limit
|
||||
|
||||
results = self._collection.map_reduce(map_f, reduce_f, **mr_args)
|
||||
results = results.find()
|
||||
if output == 'inline' and not self._ordering:
|
||||
map_reduce_function = 'inline_map_reduce'
|
||||
else:
|
||||
map_reduce_function = 'map_reduce'
|
||||
mr_args['out'] = output
|
||||
|
||||
results = getattr(self._collection, map_reduce_function)(map_f, reduce_f, **mr_args)
|
||||
|
||||
if map_reduce_function == 'map_reduce':
|
||||
results = results.find()
|
||||
|
||||
if self._ordering:
|
||||
results = results.sort(self._ordering)
|
||||
@ -777,7 +794,7 @@ class QuerySet(object):
|
||||
self._skip, self._limit = key.start, key.stop
|
||||
except IndexError, err:
|
||||
# PyMongo raises an error if key.start == key.stop, catch it,
|
||||
# bin it, kill it.
|
||||
# bin it, kill it.
|
||||
start = key.start or 0
|
||||
if start >= 0 and key.stop >= 0 and key.step is None:
|
||||
if start == key.stop:
|
||||
@ -933,7 +950,7 @@ class QuerySet(object):
|
||||
return mongo_update
|
||||
|
||||
def update(self, safe_update=True, upsert=False, **update):
|
||||
"""Perform an atomic update on the fields matched by the query. When
|
||||
"""Perform an atomic update on the fields matched by the query. When
|
||||
``safe_update`` is used, the number of affected documents is returned.
|
||||
|
||||
:param safe: check if the operation succeeded before returning
|
||||
@ -957,7 +974,7 @@ class QuerySet(object):
|
||||
raise OperationError(u'Update failed (%s)' % unicode(err))
|
||||
|
||||
def update_one(self, safe_update=True, upsert=False, **update):
|
||||
"""Perform an atomic update on first field matched by the query. When
|
||||
"""Perform an atomic update on first field matched by the query. When
|
||||
``safe_update`` is used, the number of affected documents is returned.
|
||||
|
||||
:param safe: check if the operation succeeded before returning
|
||||
@ -985,8 +1002,8 @@ class QuerySet(object):
|
||||
return self
|
||||
|
||||
def _sub_js_fields(self, code):
|
||||
"""When fields are specified with [~fieldname] syntax, where
|
||||
*fieldname* is the Python name of a field, *fieldname* will be
|
||||
"""When fields are specified with [~fieldname] syntax, where
|
||||
*fieldname* is the Python name of a field, *fieldname* will be
|
||||
substituted for the MongoDB name of the field (specified using the
|
||||
:attr:`name` keyword argument in a field's constructor).
|
||||
"""
|
||||
@ -1009,9 +1026,9 @@ class QuerySet(object):
|
||||
options specified as keyword arguments.
|
||||
|
||||
As fields in MongoEngine may use different names in the database (set
|
||||
using the :attr:`db_field` keyword argument to a :class:`Field`
|
||||
using the :attr:`db_field` keyword argument to a :class:`Field`
|
||||
constructor), a mechanism exists for replacing MongoEngine field names
|
||||
with the database field names in Javascript code. When accessing a
|
||||
with the database field names in Javascript code. When accessing a
|
||||
field, use square-bracket notation, and prefix the MongoEngine field
|
||||
name with a tilde (~).
|
||||
|
||||
@ -1037,7 +1054,7 @@ class QuerySet(object):
|
||||
query['$where'] = self._where_clause
|
||||
|
||||
scope['query'] = query
|
||||
code = pymongo.code.Code(code, scope=scope)
|
||||
code = bson.code.Code(code, scope=scope)
|
||||
|
||||
db = _get_db()
|
||||
return db.eval(code, *fields)
|
||||
|
@ -1,5 +1,6 @@
|
||||
import unittest
|
||||
from datetime import datetime
|
||||
import bson
|
||||
import pymongo
|
||||
|
||||
from mongoengine import *
|
||||
@ -7,7 +8,7 @@ from mongoengine.connection import _get_db
|
||||
|
||||
|
||||
class DocumentTest(unittest.TestCase):
|
||||
|
||||
|
||||
def setUp(self):
|
||||
connect(db='mongoenginetest')
|
||||
self.db = _get_db()
|
||||
@ -38,7 +39,7 @@ class DocumentTest(unittest.TestCase):
|
||||
name = name_field
|
||||
age = age_field
|
||||
non_field = True
|
||||
|
||||
|
||||
self.assertEqual(Person._fields['name'], name_field)
|
||||
self.assertEqual(Person._fields['age'], age_field)
|
||||
self.assertFalse('non_field' in Person._fields)
|
||||
@ -60,7 +61,7 @@ class DocumentTest(unittest.TestCase):
|
||||
|
||||
mammal_superclasses = {'Animal': Animal}
|
||||
self.assertEqual(Mammal._superclasses, mammal_superclasses)
|
||||
|
||||
|
||||
dog_superclasses = {
|
||||
'Animal': Animal,
|
||||
'Animal.Mammal': Mammal,
|
||||
@ -68,7 +69,7 @@ class DocumentTest(unittest.TestCase):
|
||||
self.assertEqual(Dog._superclasses, dog_superclasses)
|
||||
|
||||
def test_get_subclasses(self):
|
||||
"""Ensure that the correct list of subclasses is retrieved by the
|
||||
"""Ensure that the correct list of subclasses is retrieved by the
|
||||
_get_subclasses method.
|
||||
"""
|
||||
class Animal(Document): pass
|
||||
@ -78,15 +79,15 @@ class DocumentTest(unittest.TestCase):
|
||||
class Dog(Mammal): pass
|
||||
|
||||
mammal_subclasses = {
|
||||
'Animal.Mammal.Dog': Dog,
|
||||
'Animal.Mammal.Dog': Dog,
|
||||
'Animal.Mammal.Human': Human
|
||||
}
|
||||
self.assertEqual(Mammal._get_subclasses(), mammal_subclasses)
|
||||
|
||||
|
||||
animal_subclasses = {
|
||||
'Animal.Fish': Fish,
|
||||
'Animal.Mammal': Mammal,
|
||||
'Animal.Mammal.Dog': Dog,
|
||||
'Animal.Mammal.Dog': Dog,
|
||||
'Animal.Mammal.Human': Human
|
||||
}
|
||||
self.assertEqual(Animal._get_subclasses(), animal_subclasses)
|
||||
@ -124,7 +125,7 @@ class DocumentTest(unittest.TestCase):
|
||||
|
||||
self.assertTrue('name' in Employee._fields)
|
||||
self.assertTrue('salary' in Employee._fields)
|
||||
self.assertEqual(Employee._meta['collection'],
|
||||
self.assertEqual(Employee._meta['collection'],
|
||||
self.Person._meta['collection'])
|
||||
|
||||
# Ensure that MRO error is not raised
|
||||
@ -146,7 +147,7 @@ class DocumentTest(unittest.TestCase):
|
||||
class Dog(Animal):
|
||||
pass
|
||||
self.assertRaises(ValueError, create_dog_class)
|
||||
|
||||
|
||||
# Check that _cls etc aren't present on simple documents
|
||||
dog = Animal(name='dog')
|
||||
dog.save()
|
||||
@ -161,7 +162,7 @@ class DocumentTest(unittest.TestCase):
|
||||
class Employee(self.Person):
|
||||
meta = {'allow_inheritance': False}
|
||||
self.assertRaises(ValueError, create_employee_class)
|
||||
|
||||
|
||||
# Test the same for embedded documents
|
||||
class Comment(EmbeddedDocument):
|
||||
content = StringField()
|
||||
@ -186,7 +187,7 @@ class DocumentTest(unittest.TestCase):
|
||||
class Person(Document):
|
||||
name = StringField()
|
||||
meta = {'collection': collection}
|
||||
|
||||
|
||||
user = Person(name="Test User")
|
||||
user.save()
|
||||
self.assertTrue(collection in self.db.collection_names())
|
||||
@ -280,7 +281,7 @@ class DocumentTest(unittest.TestCase):
|
||||
tags = ListField(StringField())
|
||||
meta = {
|
||||
'indexes': [
|
||||
'-date',
|
||||
'-date',
|
||||
'tags',
|
||||
('category', '-date')
|
||||
],
|
||||
@ -296,12 +297,12 @@ class DocumentTest(unittest.TestCase):
|
||||
list(BlogPost.objects)
|
||||
info = BlogPost.objects._collection.index_information()
|
||||
info = [value['key'] for key, value in info.iteritems()]
|
||||
self.assertTrue([('_types', 1), ('category', 1), ('addDate', -1)]
|
||||
self.assertTrue([('_types', 1), ('category', 1), ('addDate', -1)]
|
||||
in info)
|
||||
self.assertTrue([('_types', 1), ('addDate', -1)] in info)
|
||||
# tags is a list field so it shouldn't have _types in the index
|
||||
self.assertTrue([('tags', 1)] in info)
|
||||
|
||||
|
||||
class ExtendedBlogPost(BlogPost):
|
||||
title = StringField()
|
||||
meta = {'indexes': ['title']}
|
||||
@ -311,7 +312,7 @@ class DocumentTest(unittest.TestCase):
|
||||
list(ExtendedBlogPost.objects)
|
||||
info = ExtendedBlogPost.objects._collection.index_information()
|
||||
info = [value['key'] for key, value in info.iteritems()]
|
||||
self.assertTrue([('_types', 1), ('category', 1), ('addDate', -1)]
|
||||
self.assertTrue([('_types', 1), ('category', 1), ('addDate', -1)]
|
||||
in info)
|
||||
self.assertTrue([('_types', 1), ('addDate', -1)] in info)
|
||||
self.assertTrue([('_types', 1), ('title', 1)] in info)
|
||||
@ -380,7 +381,7 @@ class DocumentTest(unittest.TestCase):
|
||||
|
||||
class EmailUser(User):
|
||||
email = StringField()
|
||||
|
||||
|
||||
user = User(username='test', name='test user')
|
||||
user.save()
|
||||
|
||||
@ -391,20 +392,20 @@ class DocumentTest(unittest.TestCase):
|
||||
user_son = User.objects._collection.find_one()
|
||||
self.assertEqual(user_son['_id'], 'test')
|
||||
self.assertTrue('username' not in user_son['_id'])
|
||||
|
||||
|
||||
User.drop_collection()
|
||||
|
||||
|
||||
user = User(pk='mongo', name='mongo user')
|
||||
user.save()
|
||||
|
||||
|
||||
user_obj = User.objects.first()
|
||||
self.assertEqual(user_obj.id, 'mongo')
|
||||
self.assertEqual(user_obj.pk, 'mongo')
|
||||
|
||||
|
||||
user_son = User.objects._collection.find_one()
|
||||
self.assertEqual(user_son['_id'], 'mongo')
|
||||
self.assertTrue('username' not in user_son['_id'])
|
||||
|
||||
|
||||
User.drop_collection()
|
||||
|
||||
def test_creation(self):
|
||||
@ -457,18 +458,18 @@ class DocumentTest(unittest.TestCase):
|
||||
"""
|
||||
class Comment(EmbeddedDocument):
|
||||
content = StringField()
|
||||
|
||||
|
||||
self.assertTrue('content' in Comment._fields)
|
||||
self.assertFalse('id' in Comment._fields)
|
||||
self.assertFalse('collection' in Comment._meta)
|
||||
|
||||
|
||||
def test_embedded_document_validation(self):
|
||||
"""Ensure that embedded documents may be validated.
|
||||
"""
|
||||
class Comment(EmbeddedDocument):
|
||||
date = DateTimeField()
|
||||
content = StringField(required=True)
|
||||
|
||||
|
||||
comment = Comment()
|
||||
self.assertRaises(ValidationError, comment.validate)
|
||||
|
||||
@ -496,7 +497,7 @@ class DocumentTest(unittest.TestCase):
|
||||
# Test skipping validation on save
|
||||
class Recipient(Document):
|
||||
email = EmailField(required=True)
|
||||
|
||||
|
||||
recipient = Recipient(email='root@localhost')
|
||||
self.assertRaises(ValidationError, recipient.save)
|
||||
try:
|
||||
@ -517,19 +518,19 @@ class DocumentTest(unittest.TestCase):
|
||||
"""Ensure that a document may be saved with a custom _id.
|
||||
"""
|
||||
# Create person object and save it to the database
|
||||
person = self.Person(name='Test User', age=30,
|
||||
person = self.Person(name='Test User', age=30,
|
||||
id='497ce96f395f2f052a494fd4')
|
||||
person.save()
|
||||
# Ensure that the object is in the database with the correct _id
|
||||
collection = self.db[self.Person._meta['collection']]
|
||||
person_obj = collection.find_one({'name': 'Test User'})
|
||||
self.assertEqual(str(person_obj['_id']), '497ce96f395f2f052a494fd4')
|
||||
|
||||
|
||||
def test_save_custom_pk(self):
|
||||
"""Ensure that a document may be saved with a custom _id using pk alias.
|
||||
"""
|
||||
# Create person object and save it to the database
|
||||
person = self.Person(name='Test User', age=30,
|
||||
person = self.Person(name='Test User', age=30,
|
||||
pk='497ce96f395f2f052a494fd4')
|
||||
person.save()
|
||||
# Ensure that the object is in the database with the correct _id
|
||||
@ -565,7 +566,7 @@ class DocumentTest(unittest.TestCase):
|
||||
BlogPost.drop_collection()
|
||||
|
||||
def test_save_embedded_document(self):
|
||||
"""Ensure that a document with an embedded document field may be
|
||||
"""Ensure that a document with an embedded document field may be
|
||||
saved in the database.
|
||||
"""
|
||||
class EmployeeDetails(EmbeddedDocument):
|
||||
@ -591,7 +592,7 @@ class DocumentTest(unittest.TestCase):
|
||||
def test_save_reference(self):
|
||||
"""Ensure that a document reference field may be saved in the database.
|
||||
"""
|
||||
|
||||
|
||||
class BlogPost(Document):
|
||||
meta = {'collection': 'blogpost_1'}
|
||||
content = StringField()
|
||||
@ -610,8 +611,8 @@ class DocumentTest(unittest.TestCase):
|
||||
post_obj = BlogPost.objects.first()
|
||||
|
||||
# Test laziness
|
||||
self.assertTrue(isinstance(post_obj._data['author'],
|
||||
pymongo.dbref.DBRef))
|
||||
self.assertTrue(isinstance(post_obj._data['author'],
|
||||
bson.dbref.DBRef))
|
||||
self.assertTrue(isinstance(post_obj.author, self.Person))
|
||||
self.assertEqual(post_obj.author.name, 'Test User')
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
import unittest
|
||||
import pymongo
|
||||
import bson
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from mongoengine.queryset import (QuerySet, MultipleObjectsReturned,
|
||||
@ -58,7 +59,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
self.assertEqual(len(people), 2)
|
||||
results = list(people)
|
||||
self.assertTrue(isinstance(results[0], self.Person))
|
||||
self.assertTrue(isinstance(results[0].id, (pymongo.objectid.ObjectId,
|
||||
self.assertTrue(isinstance(results[0].id, (bson.objectid.ObjectId,
|
||||
str, unicode)))
|
||||
self.assertEqual(results[0].name, "User A")
|
||||
self.assertEqual(results[0].age, 20)
|
||||
@ -162,7 +163,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
|
||||
person = self.Person.objects.get(age__lt=30)
|
||||
self.assertEqual(person.name, "User A")
|
||||
|
||||
|
||||
def test_find_array_position(self):
|
||||
"""Ensure that query by array position works.
|
||||
"""
|
||||
@ -177,7 +178,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
posts = ListField(EmbeddedDocumentField(Post))
|
||||
|
||||
Blog.drop_collection()
|
||||
|
||||
|
||||
Blog.objects.create(tags=['a', 'b'])
|
||||
self.assertEqual(len(Blog.objects(tags__0='a')), 1)
|
||||
self.assertEqual(len(Blog.objects(tags__0='b')), 0)
|
||||
@ -226,16 +227,16 @@ class QuerySetTest(unittest.TestCase):
|
||||
person, created = self.Person.objects.get_or_create(age=30)
|
||||
self.assertEqual(person.name, "User B")
|
||||
self.assertEqual(created, False)
|
||||
|
||||
|
||||
person, created = self.Person.objects.get_or_create(age__lt=30)
|
||||
self.assertEqual(person.name, "User A")
|
||||
self.assertEqual(created, False)
|
||||
|
||||
|
||||
# Try retrieving when no objects exists - new doc should be created
|
||||
kwargs = dict(age=50, defaults={'name': 'User C'})
|
||||
person, created = self.Person.objects.get_or_create(**kwargs)
|
||||
self.assertEqual(created, True)
|
||||
|
||||
|
||||
person = self.Person.objects.get(age=50)
|
||||
self.assertEqual(person.name, "User C")
|
||||
|
||||
@ -328,7 +329,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
self.assertEqual(obj, person)
|
||||
obj = self.Person.objects(Q(name__iexact='gUIDO VAN rOSSU')).first()
|
||||
self.assertEqual(obj, None)
|
||||
|
||||
|
||||
# Test unsafe expressions
|
||||
person = self.Person(name='Guido van Rossum [.\'Geek\']')
|
||||
person.save()
|
||||
@ -559,7 +560,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
|
||||
obj = self.Person.objects(Q(name__not=re.compile('^bob'))).first()
|
||||
self.assertEqual(obj, person)
|
||||
|
||||
|
||||
obj = self.Person.objects(Q(name__not=re.compile('^Gui'))).first()
|
||||
self.assertEqual(obj, None)
|
||||
|
||||
@ -631,7 +632,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
|
||||
class BlogPost(Document):
|
||||
name = StringField(db_field='doc-name')
|
||||
comments = ListField(EmbeddedDocumentField(Comment),
|
||||
comments = ListField(EmbeddedDocumentField(Comment),
|
||||
db_field='cmnts')
|
||||
|
||||
BlogPost.drop_collection()
|
||||
@ -733,7 +734,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
BlogPost.objects.update_one(add_to_set__tags='unique')
|
||||
post.reload()
|
||||
self.assertEqual(post.tags.count('unique'), 1)
|
||||
|
||||
|
||||
BlogPost.drop_collection()
|
||||
|
||||
def test_update_pull(self):
|
||||
@ -802,7 +803,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
"""
|
||||
|
||||
# run a map/reduce operation spanning all posts
|
||||
results = BlogPost.objects.map_reduce(map_f, reduce_f)
|
||||
results = BlogPost.objects.map_reduce(map_f, reduce_f, "myresults")
|
||||
results = list(results)
|
||||
self.assertEqual(len(results), 4)
|
||||
|
||||
@ -813,7 +814,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
self.assertEqual(film.value, 3)
|
||||
|
||||
BlogPost.drop_collection()
|
||||
|
||||
|
||||
def test_map_reduce_with_custom_object_ids(self):
|
||||
"""Ensure that QuerySet.map_reduce works properly with custom
|
||||
primary keys.
|
||||
@ -822,24 +823,24 @@ class QuerySetTest(unittest.TestCase):
|
||||
class BlogPost(Document):
|
||||
title = StringField(primary_key=True)
|
||||
tags = ListField(StringField())
|
||||
|
||||
|
||||
post1 = BlogPost(title="Post #1", tags=["mongodb", "mongoengine"])
|
||||
post2 = BlogPost(title="Post #2", tags=["django", "mongodb"])
|
||||
post3 = BlogPost(title="Post #3", tags=["hitchcock films"])
|
||||
|
||||
|
||||
post1.save()
|
||||
post2.save()
|
||||
post3.save()
|
||||
|
||||
|
||||
self.assertEqual(BlogPost._fields['title'].db_field, '_id')
|
||||
self.assertEqual(BlogPost._meta['id_field'], 'title')
|
||||
|
||||
|
||||
map_f = """
|
||||
function() {
|
||||
emit(this._id, 1);
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
# reduce to a list of tag ids and counts
|
||||
reduce_f = """
|
||||
function(key, values) {
|
||||
@ -850,10 +851,10 @@ class QuerySetTest(unittest.TestCase):
|
||||
return total;
|
||||
}
|
||||
"""
|
||||
|
||||
results = BlogPost.objects.map_reduce(map_f, reduce_f)
|
||||
|
||||
results = BlogPost.objects.map_reduce(map_f, reduce_f, "myresults")
|
||||
results = list(results)
|
||||
|
||||
|
||||
self.assertEqual(results[0].object, post1)
|
||||
self.assertEqual(results[1].object, post2)
|
||||
self.assertEqual(results[2].object, post3)
|
||||
@ -943,7 +944,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
|
||||
finalize_f = """
|
||||
function(key, value) {
|
||||
// f(sec_since_epoch,y,z) =
|
||||
// f(sec_since_epoch,y,z) =
|
||||
// log10(z) + ((y*sec_since_epoch) / 45000)
|
||||
z_10 = Math.log(value.z) / Math.log(10);
|
||||
weight = z_10 + ((value.y * value.t_s) / 45000);
|
||||
@ -962,6 +963,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
results = Link.objects.order_by("-value")
|
||||
results = results.map_reduce(map_f,
|
||||
reduce_f,
|
||||
"myresults",
|
||||
finalize_f=finalize_f,
|
||||
scope=scope)
|
||||
results = list(results)
|
||||
@ -1289,12 +1291,12 @@ class QuerySetTest(unittest.TestCase):
|
||||
title = StringField()
|
||||
date = DateTimeField()
|
||||
location = GeoPointField()
|
||||
|
||||
|
||||
def __unicode__(self):
|
||||
return self.title
|
||||
|
||||
|
||||
Event.drop_collection()
|
||||
|
||||
|
||||
event1 = Event(title="Coltrane Motion @ Double Door",
|
||||
date=datetime.now() - timedelta(days=1),
|
||||
location=[41.909889, -87.677137])
|
||||
@ -1304,7 +1306,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
event3 = Event(title="Coltrane Motion @ Empty Bottle",
|
||||
date=datetime.now(),
|
||||
location=[41.900474, -87.686638])
|
||||
|
||||
|
||||
event1.save()
|
||||
event2.save()
|
||||
event3.save()
|
||||
@ -1324,24 +1326,24 @@ class QuerySetTest(unittest.TestCase):
|
||||
self.assertTrue(event2 not in events)
|
||||
self.assertTrue(event1 in events)
|
||||
self.assertTrue(event3 in events)
|
||||
|
||||
|
||||
# ensure ordering is respected by "near"
|
||||
events = Event.objects(location__near=[41.9120459, -87.67892])
|
||||
events = events.order_by("-date")
|
||||
self.assertEqual(events.count(), 3)
|
||||
self.assertEqual(list(events), [event3, event1, event2])
|
||||
|
||||
|
||||
# find events around san francisco
|
||||
point_and_distance = [[37.7566023, -122.415579], 10]
|
||||
events = Event.objects(location__within_distance=point_and_distance)
|
||||
self.assertEqual(events.count(), 1)
|
||||
self.assertEqual(events[0], event2)
|
||||
|
||||
|
||||
# find events within 1 mile of greenpoint, broolyn, nyc, ny
|
||||
point_and_distance = [[40.7237134, -73.9509714], 1]
|
||||
events = Event.objects(location__within_distance=point_and_distance)
|
||||
self.assertEqual(events.count(), 0)
|
||||
|
||||
|
||||
# ensure ordering is respected by "within_distance"
|
||||
point_and_distance = [[41.9120459, -87.67892], 10]
|
||||
events = Event.objects(location__within_distance=point_and_distance)
|
||||
@ -1354,7 +1356,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
events = Event.objects(location__within_box=box)
|
||||
self.assertEqual(events.count(), 1)
|
||||
self.assertEqual(events[0].id, event2.id)
|
||||
|
||||
|
||||
Event.drop_collection()
|
||||
|
||||
def test_custom_querysets(self):
|
||||
@ -1398,7 +1400,7 @@ class QTest(unittest.TestCase):
|
||||
|
||||
query = {'age': {'$gte': 18}, 'name': 'test'}
|
||||
self.assertEqual((q1 & q2 & q3 & q4 & q5).to_query(Person), query)
|
||||
|
||||
|
||||
def test_q_with_dbref(self):
|
||||
"""Ensure Q objects handle DBRefs correctly"""
|
||||
connect(db='mongoenginetest')
|
||||
@ -1440,7 +1442,7 @@ class QTest(unittest.TestCase):
|
||||
query = Q(x__lt=100) & Q(y__ne='NotMyString')
|
||||
query &= Q(y__in=['a', 'b', 'c']) & Q(x__gt=-100)
|
||||
mongo_query = {
|
||||
'x': {'$lt': 100, '$gt': -100},
|
||||
'x': {'$lt': 100, '$gt': -100},
|
||||
'y': {'$ne': 'NotMyString', '$in': ['a', 'b', 'c']},
|
||||
}
|
||||
self.assertEqual(query.to_query(TestDoc), mongo_query)
|
||||
|
Loading…
x
Reference in New Issue
Block a user