merged hmarr's updates
This commit is contained in:
commit
8ad0df41a0
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
*.pyc
|
*.pyc
|
||||||
.*.swp
|
.*.swp
|
||||||
docs/.build
|
docs/.build
|
||||||
|
docs/_build
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
SPHINXOPTS =
|
SPHINXOPTS =
|
||||||
SPHINXBUILD = sphinx-build
|
SPHINXBUILD = sphinx-build
|
||||||
PAPER =
|
PAPER =
|
||||||
BUILDDIR = .build
|
BUILDDIR = _build
|
||||||
|
|
||||||
# Internal variables.
|
# Internal variables.
|
||||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||||
|
46
docs/apireference.rst
Normal file
46
docs/apireference.rst
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
API Reference
|
||||||
|
=============
|
||||||
|
|
||||||
|
Connecting
|
||||||
|
----------
|
||||||
|
|
||||||
|
.. autofunction:: mongoengine.connect
|
||||||
|
|
||||||
|
Documents
|
||||||
|
---------
|
||||||
|
|
||||||
|
.. autoclass:: mongoengine.Document
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. attribute:: objects
|
||||||
|
|
||||||
|
A :class:`~mongoengine.queryset.QuerySet` object that is created lazily
|
||||||
|
on access.
|
||||||
|
|
||||||
|
.. autoclass:: mongoengine.EmbeddedDocument
|
||||||
|
:members:
|
||||||
|
|
||||||
|
Querying
|
||||||
|
--------
|
||||||
|
|
||||||
|
.. autoclass:: mongoengine.queryset.QuerySet
|
||||||
|
:members:
|
||||||
|
|
||||||
|
Fields
|
||||||
|
------
|
||||||
|
|
||||||
|
.. autoclass:: mongoengine.StringField
|
||||||
|
|
||||||
|
.. autoclass:: mongoengine.IntField
|
||||||
|
|
||||||
|
.. autoclass:: mongoengine.FloatField
|
||||||
|
|
||||||
|
.. autoclass:: mongoengine.DateTimeField
|
||||||
|
|
||||||
|
.. autoclass:: mongoengine.EmbeddedDocumentField
|
||||||
|
|
||||||
|
.. autoclass:: mongoengine.ListField
|
||||||
|
|
||||||
|
.. autoclass:: mongoengine.ObjectIdField
|
||||||
|
|
||||||
|
.. autoclass:: mongoengine.ReferenceField
|
10
docs/conf.py
10
docs/conf.py
@ -16,13 +16,13 @@ import sys, os
|
|||||||
# If extensions (or modules to document with autodoc) are in another directory,
|
# If extensions (or modules to document with autodoc) are in another directory,
|
||||||
# add these directories to sys.path here. If the directory is relative to the
|
# add these directories to sys.path here. If the directory is relative to the
|
||||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
#sys.path.append(os.path.abspath('.'))
|
sys.path.append(os.path.abspath('..'))
|
||||||
|
|
||||||
# -- General configuration -----------------------------------------------------
|
# -- General configuration -----------------------------------------------------
|
||||||
|
|
||||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||||
extensions = []
|
extensions = ['sphinx.ext.autodoc']
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
templates_path = ['.templates']
|
templates_path = ['.templates']
|
||||||
@ -64,7 +64,7 @@ release = '0.1'
|
|||||||
|
|
||||||
# List of directories, relative to source directory, that shouldn't be searched
|
# List of directories, relative to source directory, that shouldn't be searched
|
||||||
# for source files.
|
# for source files.
|
||||||
exclude_trees = ['.build']
|
exclude_trees = ['_build']
|
||||||
|
|
||||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||||
#default_role = None
|
#default_role = None
|
||||||
@ -99,7 +99,7 @@ html_theme = 'nature'
|
|||||||
#html_theme_options = {}
|
#html_theme_options = {}
|
||||||
|
|
||||||
# Add any paths that contain custom themes here, relative to this directory.
|
# Add any paths that contain custom themes here, relative to this directory.
|
||||||
html_theme_path = ['.themes']
|
html_theme_path = ['_themes']
|
||||||
|
|
||||||
# The name for this set of Sphinx documents. If None, it defaults to
|
# The name for this set of Sphinx documents. If None, it defaults to
|
||||||
# "<project> v<release> documentation".
|
# "<project> v<release> documentation".
|
||||||
@ -120,7 +120,7 @@ html_theme_path = ['.themes']
|
|||||||
# Add any paths that contain custom static files (such as style sheets) here,
|
# Add any paths that contain custom static files (such as style sheets) here,
|
||||||
# relative to this directory. They are copied after the builtin static files,
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
html_static_path = ['.static']
|
html_static_path = ['_static']
|
||||||
|
|
||||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||||
# using the given strftime format.
|
# using the given strftime format.
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
You can adapt this file completely to your liking, but it should at least
|
You can adapt this file completely to your liking, but it should at least
|
||||||
contain the root `toctree` directive.
|
contain the root `toctree` directive.
|
||||||
|
|
||||||
Welcome to MongoEngine's documentation!
|
MongoEngine User Documentation
|
||||||
=======================================
|
=======================================
|
||||||
|
|
||||||
Contents:
|
Contents:
|
||||||
@ -12,6 +12,7 @@ Contents:
|
|||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
tutorial.rst
|
tutorial.rst
|
||||||
|
apireference.rst
|
||||||
|
|
||||||
Indices and tables
|
Indices and tables
|
||||||
==================
|
==================
|
||||||
|
@ -136,20 +136,36 @@ class TopLevelDocumentMetaclass(DocumentMetaclass):
|
|||||||
if attrs.get('__metaclass__') == TopLevelDocumentMetaclass:
|
if attrs.get('__metaclass__') == TopLevelDocumentMetaclass:
|
||||||
return super_new(cls, name, bases, attrs)
|
return super_new(cls, name, bases, attrs)
|
||||||
|
|
||||||
collection = attrs.get('__collection__', name.lower())
|
collection = name.lower()
|
||||||
|
|
||||||
|
simple_class = True
|
||||||
|
|
||||||
# Subclassed documents inherit collection from superclass
|
# Subclassed documents inherit collection from superclass
|
||||||
for base in bases:
|
for base in bases:
|
||||||
if hasattr(base, '_meta') and 'collection' in base._meta:
|
if hasattr(base, '_meta') and 'collection' in base._meta:
|
||||||
|
# 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' %
|
||||||
|
base.__name__)
|
||||||
|
else:
|
||||||
|
simple_class = False
|
||||||
collection = base._meta['collection']
|
collection = base._meta['collection']
|
||||||
|
|
||||||
meta = {
|
meta = {
|
||||||
'collection': collection,
|
'collection': collection,
|
||||||
|
'allow_inheritance': True,
|
||||||
}
|
}
|
||||||
meta.update(attrs.get('meta', {}))
|
meta.update(attrs.get('meta', {}))
|
||||||
|
# Only simple classes - direct subclasses of Document - may set
|
||||||
|
# allow_inheritance to False
|
||||||
|
if not simple_class and not meta['allow_inheritance']:
|
||||||
|
raise ValueError('Only direct subclasses of Document may set '
|
||||||
|
'"allow_inheritance" to False')
|
||||||
attrs['_meta'] = meta
|
attrs['_meta'] = meta
|
||||||
|
|
||||||
attrs['_id'] = ObjectIdField()
|
attrs['id'] = ObjectIdField(name='_id')
|
||||||
|
|
||||||
# Set up collection manager, needs the class to have fields so use
|
# Set up collection manager, needs the class to have fields so use
|
||||||
# DocumentMetaclass before instantiating CollectionManager object
|
# DocumentMetaclass before instantiating CollectionManager object
|
||||||
@ -168,10 +184,11 @@ class BaseDocument(object):
|
|||||||
if attr_name in values:
|
if attr_name in values:
|
||||||
setattr(self, attr_name, values.pop(attr_name))
|
setattr(self, attr_name, values.pop(attr_name))
|
||||||
else:
|
else:
|
||||||
if attr_value.required:
|
# Use default value if present
|
||||||
|
value = getattr(self, attr_name, None)
|
||||||
|
if value is None and attr_value.required:
|
||||||
raise ValidationError('Field "%s" is required' % attr_name)
|
raise ValidationError('Field "%s" is required' % attr_name)
|
||||||
# Use default value
|
setattr(self, attr_name, value)
|
||||||
setattr(self, attr_name, getattr(self, attr_name, None))
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_subclasses(cls):
|
def _get_subclasses(cls):
|
||||||
@ -226,9 +243,12 @@ class BaseDocument(object):
|
|||||||
for field_name, field in self._fields.items():
|
for field_name, field in self._fields.items():
|
||||||
value = getattr(self, field_name, None)
|
value = getattr(self, field_name, None)
|
||||||
if value is not None:
|
if value is not None:
|
||||||
data[field_name] = field.to_mongo(value)
|
data[field.name] = field.to_mongo(value)
|
||||||
data['_cls'] = self._class_name
|
# Only add _cls and _types if allow_inheritance is not False
|
||||||
data['_types'] = self._superclasses.keys() + [self._class_name]
|
if not (hasattr(self, '_meta') and
|
||||||
|
self._meta.get('allow_inheritance', True) == False):
|
||||||
|
data['_cls'] = self._class_name
|
||||||
|
data['_types'] = self._superclasses.keys() + [self._class_name]
|
||||||
return data
|
return data
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -241,6 +261,9 @@ class BaseDocument(object):
|
|||||||
|
|
||||||
data = dict((str(key), value) for key, value in son.items())
|
data = dict((str(key), value) for key, value in son.items())
|
||||||
|
|
||||||
|
if '_types' in data:
|
||||||
|
del data['_types']
|
||||||
|
|
||||||
if '_cls' in data:
|
if '_cls' in data:
|
||||||
del data['_cls']
|
del data['_cls']
|
||||||
|
|
||||||
@ -254,7 +277,7 @@ class BaseDocument(object):
|
|||||||
cls = subclasses[class_name]
|
cls = subclasses[class_name]
|
||||||
|
|
||||||
for field_name, field in cls._fields.items():
|
for field_name, field in cls._fields.items():
|
||||||
if field_name in data:
|
if field.name in data:
|
||||||
data[field_name] = field.to_python(data[field_name])
|
data[field_name] = field.to_python(data[field.name])
|
||||||
|
|
||||||
return cls(**data)
|
return cls(**data)
|
||||||
|
@ -6,25 +6,58 @@ __all__ = ['Document', 'EmbeddedDocument']
|
|||||||
|
|
||||||
|
|
||||||
class EmbeddedDocument(BaseDocument):
|
class EmbeddedDocument(BaseDocument):
|
||||||
|
"""A :class:`~mongoengine.Document` that isn't stored in its own
|
||||||
|
collection. :class:`~mongoengine.EmbeddedDocument`\ s should be used as
|
||||||
|
fields on :class:`~mongoengine.Document`\ s through the
|
||||||
|
:class:`~mongoengine.EmbeddedDocumentField` field type.
|
||||||
|
"""
|
||||||
|
|
||||||
__metaclass__ = DocumentMetaclass
|
__metaclass__ = DocumentMetaclass
|
||||||
|
|
||||||
|
|
||||||
class Document(BaseDocument):
|
class Document(BaseDocument):
|
||||||
|
"""The base class used for defining the structure and properties of
|
||||||
|
collections of documents stored in MongoDB. Inherit from this class, and
|
||||||
|
add fields as class attributes to define a document's structure.
|
||||||
|
Individual documents may then be created by making instances of the
|
||||||
|
:class:`~mongoengine.Document` subclass.
|
||||||
|
|
||||||
|
By default, the MongoDB collection used to store documents created using a
|
||||||
|
:class:`~mongoengine.Document` subclass will be the name of the subclass
|
||||||
|
converted to lowercase. A different collection may be specified by
|
||||||
|
providing :attr:`collection` to the :attr:`meta` dictionary in the class
|
||||||
|
definition.
|
||||||
|
|
||||||
|
A :class:`~mongoengine.Document` subclass may be itself subclassed, to
|
||||||
|
create a specialised version of the document that will be stored in the
|
||||||
|
same collection. To facilitate this behaviour, `_cls` and `_types`
|
||||||
|
fields are added to documents (hidden though the MongoEngine interface
|
||||||
|
though). To disable this behaviour and remove the dependence on the
|
||||||
|
presence of `_cls` and `_types`, set :attr:`allow_inheritance` to
|
||||||
|
``False`` in the :attr:`meta` dictionary.
|
||||||
|
"""
|
||||||
|
|
||||||
__metaclass__ = TopLevelDocumentMetaclass
|
__metaclass__ = TopLevelDocumentMetaclass
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
"""Save the document to the database. If the document already exists,
|
"""Save the :class:`~mongoengine.Document` to the database. If the
|
||||||
it will be updated, otherwise it will be created.
|
document already exists, it will be updated, otherwise it will be
|
||||||
|
created.
|
||||||
"""
|
"""
|
||||||
_id = self.objects._collection.save(self.to_mongo())
|
object_id = self.objects._collection.save(self.to_mongo())
|
||||||
self._id = _id
|
self.id = object_id
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
"""Delete the :class:`~mongoengine.Document` from the database. This
|
||||||
|
will only take effect if the document has been previously saved.
|
||||||
|
"""
|
||||||
|
object_id = self._fields['id'].to_mongo(self.id)
|
||||||
|
self.__class__.objects(_id=object_id).delete()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def drop_collection(cls):
|
def drop_collection(cls):
|
||||||
"""Drops the entire collection associated with this Document type from
|
"""Drops the entire collection associated with this
|
||||||
the database.
|
:class:`~mongoengine.Document` type from the database.
|
||||||
"""
|
"""
|
||||||
db = _get_db()
|
db = _get_db()
|
||||||
db.drop_collection(cls._meta['collection'])
|
db.drop_collection(cls._meta['collection'])
|
||||||
|
@ -87,7 +87,7 @@ class DateTimeField(BaseField):
|
|||||||
|
|
||||||
class EmbeddedDocumentField(BaseField):
|
class EmbeddedDocumentField(BaseField):
|
||||||
"""An embedded document field. Only valid values are subclasses of
|
"""An embedded document field. Only valid values are subclasses of
|
||||||
EmbeddedDocument.
|
:class:`~mongoengine.EmbeddedDocument`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, document, **kwargs):
|
def __init__(self, document, **kwargs):
|
||||||
@ -179,19 +179,19 @@ class ReferenceField(BaseField):
|
|||||||
|
|
||||||
def to_mongo(self, document):
|
def to_mongo(self, document):
|
||||||
if isinstance(document, (str, unicode, pymongo.objectid.ObjectId)):
|
if isinstance(document, (str, unicode, pymongo.objectid.ObjectId)):
|
||||||
_id = document
|
id_ = document
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
_id = document._id
|
id_ = document.id
|
||||||
except:
|
except:
|
||||||
raise ValidationError('You can only reference documents once '
|
raise ValidationError('You can only reference documents once '
|
||||||
'they have been saved to the database')
|
'they have been saved to the database')
|
||||||
|
|
||||||
if not isinstance(_id, pymongo.objectid.ObjectId):
|
if not isinstance(id_, pymongo.objectid.ObjectId):
|
||||||
_id = pymongo.objectid.ObjectId(_id)
|
id_ = pymongo.objectid.ObjectId(id_)
|
||||||
|
|
||||||
collection = self.document_type._meta['collection']
|
collection = self.document_type._meta['collection']
|
||||||
return pymongo.dbref.DBRef(collection, _id)
|
return pymongo.dbref.DBRef(collection, id_)
|
||||||
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
assert(isinstance(value, (self.document_type, pymongo.dbref.DBRef)))
|
assert(isinstance(value, (self.document_type, pymongo.dbref.DBRef)))
|
||||||
|
@ -5,15 +5,19 @@ import pymongo
|
|||||||
|
|
||||||
class QuerySet(object):
|
class QuerySet(object):
|
||||||
"""A set of results returned from a query. Wraps a MongoDB cursor,
|
"""A set of results returned from a query. Wraps a MongoDB cursor,
|
||||||
providing Document objects as the results.
|
providing :class:`~mongoengine.Document` objects as the results.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, document, collection):
|
def __init__(self, document, collection):
|
||||||
self._document = document
|
self._document = document
|
||||||
self._collection = collection
|
self._collection = collection
|
||||||
self._query = {}
|
self._query = {}
|
||||||
|
|
||||||
|
# If inheritance is allowed, only return instances and instances of
|
||||||
|
# subclasses of the class being used
|
||||||
|
if document._meta.get('allow_inheritance'):
|
||||||
|
self._query = {'_types': self._document._class_name}
|
||||||
self._cursor_obj = None
|
self._cursor_obj = None
|
||||||
self._ordering = []
|
|
||||||
|
|
||||||
def ensure_index(self, key_or_list, direction=None):
|
def ensure_index(self, key_or_list, direction=None):
|
||||||
"""Ensure that the given indexes are in place.
|
"""Ensure that the given indexes are in place.
|
||||||
@ -29,7 +33,8 @@ class QuerySet(object):
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
def __call__(self, **query):
|
def __call__(self, **query):
|
||||||
"""Filter the selected documents by calling the queryset with a query.
|
"""Filter the selected documents by calling the
|
||||||
|
:class:`~mongoengine.QuerySet` with a query.
|
||||||
"""
|
"""
|
||||||
self._query.update(QuerySet._transform_query(**query))
|
self._query.update(QuerySet._transform_query(**query))
|
||||||
return self
|
return self
|
||||||
@ -73,7 +78,7 @@ class QuerySet(object):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
def with_id(self, object_id):
|
def with_id(self, object_id):
|
||||||
"""Retrieve the object matching the _id provided.
|
"""Retrieve the object matching the id provided.
|
||||||
"""
|
"""
|
||||||
if not isinstance(object_id, pymongo.objectid.ObjectId):
|
if not isinstance(object_id, pymongo.objectid.ObjectId):
|
||||||
object_id = pymongo.objectid.ObjectId(object_id)
|
object_id = pymongo.objectid.ObjectId(object_id)
|
||||||
@ -84,7 +89,7 @@ class QuerySet(object):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
def next(self):
|
def next(self):
|
||||||
"""Wrap the result in a Document object.
|
"""Wrap the result in a :class:`~mongoengine.Document` object.
|
||||||
"""
|
"""
|
||||||
return self._document._from_son(self._cursor.next())
|
return self._document._from_son(self._cursor.next())
|
||||||
|
|
||||||
@ -94,41 +99,54 @@ class QuerySet(object):
|
|||||||
return self._cursor.count()
|
return self._cursor.count()
|
||||||
|
|
||||||
def limit(self, n):
|
def limit(self, n):
|
||||||
"""Limit the number of returned documents to.
|
"""Limit the number of returned documents to `n`. This may also be
|
||||||
|
achieved using array-slicing syntax (e.g. ``User.objects[:5]``).
|
||||||
"""
|
"""
|
||||||
self._cursor.limit(n)
|
self._cursor.limit(n)
|
||||||
# Return self to allow chaining
|
# Return self to allow chaining
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def skip(self, n):
|
def skip(self, n):
|
||||||
"""Skip n documents before returning the results.
|
"""Skip `n` documents before returning the results. This may also be
|
||||||
|
achieved using array-slicing syntax (e.g. ``User.objects[5:]``).
|
||||||
"""
|
"""
|
||||||
self._cursor.skip(n)
|
self._cursor.skip(n)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def order_by(self, *params):
|
|
||||||
"""Apply ordering conditions, Django-style.
|
|
||||||
|
|
||||||
e.g., ``Model.objects.().order_by("-published_date", "ordering")``
|
|
||||||
will order first by ``published_date DESC``, and then ``ordering ASC``.
|
|
||||||
|
|
||||||
"""
|
|
||||||
if not params:
|
|
||||||
self._ordering = []
|
|
||||||
for param in params:
|
|
||||||
if param.startswith("-"):
|
|
||||||
param = param[1:]
|
|
||||||
sort_dir = pymongo.DESCENDING
|
|
||||||
else:
|
|
||||||
sort_dir = pymongo.ASCENDING
|
|
||||||
sort_rule = (param, sort_dir)
|
|
||||||
|
|
||||||
if not sort_rule in self._ordering:
|
def __getitem__(self, key):
|
||||||
self._ordering.append(sort_rule)
|
"""Support skip and limit using getitem and slicing syntax.
|
||||||
self._cursor.sort(self._ordering)
|
"""
|
||||||
|
# Slice provided
|
||||||
|
if isinstance(key, slice):
|
||||||
|
self._cursor_obj = self._cursor[key]
|
||||||
|
# Allow further QuerySet modifications to be performed
|
||||||
|
return self
|
||||||
|
# Integer index provided
|
||||||
|
elif isinstance(key, int):
|
||||||
|
return self._document._from_son(self._cursor[key])
|
||||||
|
|
||||||
|
def order_by(self, *keys):
|
||||||
|
"""Order the :class:`~mongoengine.queryset.QuerySet` by the keys. The
|
||||||
|
order may be specified by prepending each of the keys by a + or a -.
|
||||||
|
Ascending order is assumed.
|
||||||
|
"""
|
||||||
|
key_list = []
|
||||||
|
for key in keys:
|
||||||
|
direction = pymongo.ASCENDING
|
||||||
|
if key[0] == '-':
|
||||||
|
direction = pymongo.DESCENDING
|
||||||
|
if key[0] in ('-', '+'):
|
||||||
|
key = key[1:]
|
||||||
|
key_list.append((key, direction))
|
||||||
|
|
||||||
|
self._cursor.sort(key_list)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def explain(self, format=False):
|
def explain(self, format=False):
|
||||||
|
"""Return an explain plan record for the
|
||||||
|
:class:`~mongoengine.queryset.QuerySet`\ 's cursor.
|
||||||
|
"""
|
||||||
|
|
||||||
plan = self._cursor.explain()
|
plan = self._cursor.explain()
|
||||||
if format:
|
if format:
|
||||||
import pprint
|
import pprint
|
||||||
|
@ -41,7 +41,7 @@ class DocumentTest(unittest.TestCase):
|
|||||||
self.assertEqual(Person._fields['name'], name_field)
|
self.assertEqual(Person._fields['name'], name_field)
|
||||||
self.assertEqual(Person._fields['age'], age_field)
|
self.assertEqual(Person._fields['age'], age_field)
|
||||||
self.assertFalse('non_field' in Person._fields)
|
self.assertFalse('non_field' in Person._fields)
|
||||||
self.assertTrue('_id' in Person._fields)
|
self.assertTrue('id' in Person._fields)
|
||||||
# Test iteration over fields
|
# Test iteration over fields
|
||||||
fields = list(Person())
|
fields = list(Person())
|
||||||
self.assertTrue('name' in fields and 'age' in fields)
|
self.assertTrue('name' in fields and 'age' in fields)
|
||||||
@ -126,6 +126,36 @@ class DocumentTest(unittest.TestCase):
|
|||||||
self.assertEqual(Employee._meta['collection'],
|
self.assertEqual(Employee._meta['collection'],
|
||||||
self.Person._meta['collection'])
|
self.Person._meta['collection'])
|
||||||
|
|
||||||
|
def test_allow_inheritance(self):
|
||||||
|
"""Ensure that inheritance may be disabled on simple classes and that
|
||||||
|
_cls and _types will not be used.
|
||||||
|
"""
|
||||||
|
class Animal(Document):
|
||||||
|
meta = {'allow_inheritance': False}
|
||||||
|
name = StringField()
|
||||||
|
|
||||||
|
Animal.drop_collection()
|
||||||
|
|
||||||
|
def create_dog_class():
|
||||||
|
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()
|
||||||
|
collection = self.db[Animal._meta['collection']]
|
||||||
|
obj = collection.find_one()
|
||||||
|
self.assertFalse('_cls' in obj)
|
||||||
|
self.assertFalse('_types' in obj)
|
||||||
|
|
||||||
|
Animal.drop_collection()
|
||||||
|
|
||||||
|
def create_employee_class():
|
||||||
|
class Employee(self.Person):
|
||||||
|
meta = {'allow_inheritance': False}
|
||||||
|
self.assertRaises(ValueError, create_employee_class)
|
||||||
|
|
||||||
def test_creation(self):
|
def test_creation(self):
|
||||||
"""Ensure that document may be created using keyword arguments.
|
"""Ensure that document may be created using keyword arguments.
|
||||||
"""
|
"""
|
||||||
@ -145,7 +175,7 @@ class DocumentTest(unittest.TestCase):
|
|||||||
person['name'] = 'Another User'
|
person['name'] = 'Another User'
|
||||||
self.assertEquals(person['name'], 'Another User')
|
self.assertEquals(person['name'], 'Another User')
|
||||||
|
|
||||||
# Length = length(assigned fields + _id)
|
# Length = length(assigned fields + id)
|
||||||
self.assertEquals(len(person), 3)
|
self.assertEquals(len(person), 3)
|
||||||
|
|
||||||
self.assertTrue('age' in person)
|
self.assertTrue('age' in person)
|
||||||
@ -160,7 +190,7 @@ class DocumentTest(unittest.TestCase):
|
|||||||
content = StringField()
|
content = StringField()
|
||||||
|
|
||||||
self.assertTrue('content' in Comment._fields)
|
self.assertTrue('content' in Comment._fields)
|
||||||
self.assertFalse('_id' in Comment._fields)
|
self.assertFalse('id' in Comment._fields)
|
||||||
self.assertFalse(hasattr(Comment, '_meta'))
|
self.assertFalse(hasattr(Comment, '_meta'))
|
||||||
|
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
@ -174,14 +204,23 @@ class DocumentTest(unittest.TestCase):
|
|||||||
person_obj = collection.find_one({'name': 'Test User'})
|
person_obj = collection.find_one({'name': 'Test User'})
|
||||||
self.assertEqual(person_obj['name'], 'Test User')
|
self.assertEqual(person_obj['name'], 'Test User')
|
||||||
self.assertEqual(person_obj['age'], 30)
|
self.assertEqual(person_obj['age'], 30)
|
||||||
self.assertEqual(person_obj['_id'], person._id)
|
self.assertEqual(person_obj['_id'], person.id)
|
||||||
|
|
||||||
|
def test_delete(self):
|
||||||
|
"""Ensure that document may be deleted using the delete method.
|
||||||
|
"""
|
||||||
|
person = self.Person(name="Test User", age=30)
|
||||||
|
person.save()
|
||||||
|
self.assertEqual(self.Person.objects.count(), 1)
|
||||||
|
person.delete()
|
||||||
|
self.assertEqual(self.Person.objects.count(), 0)
|
||||||
|
|
||||||
def test_save_custom_id(self):
|
def test_save_custom_id(self):
|
||||||
"""Ensure that a document may be saved with a custom _id.
|
"""Ensure that a document may be saved with a custom _id.
|
||||||
"""
|
"""
|
||||||
# Create person object and save it to the database
|
# 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')
|
id='497ce96f395f2f052a494fd4')
|
||||||
person.save()
|
person.save()
|
||||||
# Ensure that the object is in the database with the correct _id
|
# Ensure that the object is in the database with the correct _id
|
||||||
collection = self.db[self.Person._meta['collection']]
|
collection = self.db[self.Person._meta['collection']]
|
||||||
@ -268,7 +307,7 @@ class DocumentTest(unittest.TestCase):
|
|||||||
post_obj.author.age = 25
|
post_obj.author.age = 25
|
||||||
post_obj.author.save()
|
post_obj.author.save()
|
||||||
|
|
||||||
author = self.Person.objects(name='Test User').first()
|
author = list(self.Person.objects(name='Test User'))[-1]
|
||||||
self.assertEqual(author.age, 25)
|
self.assertEqual(author.age, 25)
|
||||||
|
|
||||||
BlogPost.drop_collection()
|
BlogPost.drop_collection()
|
||||||
|
@ -46,10 +46,10 @@ class FieldTest(unittest.TestCase):
|
|||||||
name = StringField()
|
name = StringField()
|
||||||
|
|
||||||
person = Person(name='Test User')
|
person = Person(name='Test User')
|
||||||
self.assertRaises(AttributeError, getattr, person, '_id')
|
self.assertRaises(AttributeError, getattr, person, 'id')
|
||||||
self.assertRaises(ValidationError, person.__setattr__, '_id', 47)
|
self.assertRaises(ValidationError, person.__setattr__, 'id', 47)
|
||||||
self.assertRaises(ValidationError, person.__setattr__, '_id', 'abc')
|
self.assertRaises(ValidationError, person.__setattr__, 'id', 'abc')
|
||||||
person._id = '497ce96f395f2f052a494fd4'
|
person.id = '497ce96f395f2f052a494fd4'
|
||||||
|
|
||||||
def test_string_validation(self):
|
def test_string_validation(self):
|
||||||
"""Ensure that invalid values cannot be assigned to string fields.
|
"""Ensure that invalid values cannot be assigned to string fields.
|
||||||
|
@ -53,7 +53,7 @@ class QuerySetTest(unittest.TestCase):
|
|||||||
self.assertEqual(people.count(), 2)
|
self.assertEqual(people.count(), 2)
|
||||||
results = list(people)
|
results = list(people)
|
||||||
self.assertTrue(isinstance(results[0], self.Person))
|
self.assertTrue(isinstance(results[0], self.Person))
|
||||||
self.assertTrue(isinstance(results[0]._id, (pymongo.objectid.ObjectId,
|
self.assertTrue(isinstance(results[0].id, (pymongo.objectid.ObjectId,
|
||||||
str, unicode)))
|
str, unicode)))
|
||||||
self.assertEqual(results[0].name, "User A")
|
self.assertEqual(results[0].name, "User A")
|
||||||
self.assertEqual(results[0].age, 20)
|
self.assertEqual(results[0].age, 20)
|
||||||
@ -77,6 +77,26 @@ class QuerySetTest(unittest.TestCase):
|
|||||||
self.assertEqual(len(people), 1)
|
self.assertEqual(len(people), 1)
|
||||||
self.assertEqual(people[0].name, 'User B')
|
self.assertEqual(people[0].name, 'User B')
|
||||||
|
|
||||||
|
person3 = self.Person(name="User C", age=40)
|
||||||
|
person3.save()
|
||||||
|
|
||||||
|
# Test slice limit
|
||||||
|
people = list(self.Person.objects[:2])
|
||||||
|
self.assertEqual(len(people), 2)
|
||||||
|
self.assertEqual(people[0].name, 'User A')
|
||||||
|
self.assertEqual(people[1].name, 'User B')
|
||||||
|
|
||||||
|
# Test slice skip
|
||||||
|
people = list(self.Person.objects[1:])
|
||||||
|
self.assertEqual(len(people), 2)
|
||||||
|
self.assertEqual(people[0].name, 'User B')
|
||||||
|
self.assertEqual(people[1].name, 'User C')
|
||||||
|
|
||||||
|
# Test slice limit and skip
|
||||||
|
people = list(self.Person.objects[1:2])
|
||||||
|
self.assertEqual(len(people), 1)
|
||||||
|
self.assertEqual(people[0].name, 'User B')
|
||||||
|
|
||||||
def test_find_one(self):
|
def test_find_one(self):
|
||||||
"""Ensure that a query using find_one returns a valid result.
|
"""Ensure that a query using find_one returns a valid result.
|
||||||
"""
|
"""
|
||||||
@ -97,9 +117,18 @@ class QuerySetTest(unittest.TestCase):
|
|||||||
|
|
||||||
person = self.Person.objects(age__lt=30).first()
|
person = self.Person.objects(age__lt=30).first()
|
||||||
self.assertEqual(person.name, "User A")
|
self.assertEqual(person.name, "User A")
|
||||||
|
|
||||||
|
# Use array syntax
|
||||||
|
person = self.Person.objects[0]
|
||||||
|
self.assertEqual(person.name, "User A")
|
||||||
|
|
||||||
|
person = self.Person.objects[1]
|
||||||
|
self.assertEqual(person.name, "User B")
|
||||||
|
|
||||||
|
self.assertRaises(IndexError, self.Person.objects.__getitem__, 2)
|
||||||
|
|
||||||
# Find a document using just the object id
|
# Find a document using just the object id
|
||||||
person = self.Person.objects.with_id(person1._id)
|
person = self.Person.objects.with_id(person1.id)
|
||||||
self.assertEqual(person.name, "User A")
|
self.assertEqual(person.name, "User A")
|
||||||
|
|
||||||
def test_find_embedded(self):
|
def test_find_embedded(self):
|
||||||
@ -137,6 +166,25 @@ class QuerySetTest(unittest.TestCase):
|
|||||||
self.Person.objects.delete()
|
self.Person.objects.delete()
|
||||||
self.assertEqual(self.Person.objects.count(), 0)
|
self.assertEqual(self.Person.objects.count(), 0)
|
||||||
|
|
||||||
|
def test_order_by(self):
|
||||||
|
"""Ensure that QuerySets may be ordered.
|
||||||
|
"""
|
||||||
|
self.Person(name="User A", age=20).save()
|
||||||
|
self.Person(name="User B", age=40).save()
|
||||||
|
self.Person(name="User C", age=30).save()
|
||||||
|
|
||||||
|
names = [p.name for p in self.Person.objects.order_by('-age')]
|
||||||
|
self.assertEqual(names, ['User B', 'User C', 'User A'])
|
||||||
|
|
||||||
|
names = [p.name for p in self.Person.objects.order_by('+age')]
|
||||||
|
self.assertEqual(names, ['User A', 'User C', 'User B'])
|
||||||
|
|
||||||
|
names = [p.name for p in self.Person.objects.order_by('age')]
|
||||||
|
self.assertEqual(names, ['User A', 'User C', 'User B'])
|
||||||
|
|
||||||
|
ages = [p.age for p in self.Person.objects.order_by('-name')]
|
||||||
|
self.assertEqual(ages, [30, 40, 20])
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.Person.drop_collection()
|
self.Person.drop_collection()
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user