merged hmarr's updates
This commit is contained in:
@@ -136,20 +136,36 @@ class TopLevelDocumentMetaclass(DocumentMetaclass):
|
||||
if attrs.get('__metaclass__') == TopLevelDocumentMetaclass:
|
||||
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
|
||||
for base in bases:
|
||||
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']
|
||||
|
||||
meta = {
|
||||
'collection': collection,
|
||||
'allow_inheritance': True,
|
||||
}
|
||||
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['_id'] = ObjectIdField()
|
||||
attrs['id'] = ObjectIdField(name='_id')
|
||||
|
||||
# Set up collection manager, needs the class to have fields so use
|
||||
# DocumentMetaclass before instantiating CollectionManager object
|
||||
@@ -168,10 +184,11 @@ class BaseDocument(object):
|
||||
if attr_name in values:
|
||||
setattr(self, attr_name, values.pop(attr_name))
|
||||
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)
|
||||
# Use default value
|
||||
setattr(self, attr_name, getattr(self, attr_name, None))
|
||||
setattr(self, attr_name, value)
|
||||
|
||||
@classmethod
|
||||
def _get_subclasses(cls):
|
||||
@@ -226,9 +243,12 @@ class BaseDocument(object):
|
||||
for field_name, field in self._fields.items():
|
||||
value = getattr(self, field_name, None)
|
||||
if value is not None:
|
||||
data[field_name] = field.to_mongo(value)
|
||||
data['_cls'] = self._class_name
|
||||
data['_types'] = self._superclasses.keys() + [self._class_name]
|
||||
data[field.name] = field.to_mongo(value)
|
||||
# Only add _cls and _types if allow_inheritance is not False
|
||||
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
|
||||
|
||||
@classmethod
|
||||
@@ -241,6 +261,9 @@ class BaseDocument(object):
|
||||
|
||||
data = dict((str(key), value) for key, value in son.items())
|
||||
|
||||
if '_types' in data:
|
||||
del data['_types']
|
||||
|
||||
if '_cls' in data:
|
||||
del data['_cls']
|
||||
|
||||
@@ -254,7 +277,7 @@ class BaseDocument(object):
|
||||
cls = subclasses[class_name]
|
||||
|
||||
for field_name, field in cls._fields.items():
|
||||
if field_name in data:
|
||||
data[field_name] = field.to_python(data[field_name])
|
||||
if field.name in data:
|
||||
data[field_name] = field.to_python(data[field.name])
|
||||
|
||||
return cls(**data)
|
||||
|
||||
@@ -6,25 +6,58 @@ __all__ = ['Document', 'EmbeddedDocument']
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
|
||||
def save(self):
|
||||
"""Save the document to the database. If the document already exists,
|
||||
it will be updated, otherwise it will be created.
|
||||
"""Save the :class:`~mongoengine.Document` to the database. If the
|
||||
document already exists, it will be updated, otherwise it will be
|
||||
created.
|
||||
"""
|
||||
_id = self.objects._collection.save(self.to_mongo())
|
||||
self._id = _id
|
||||
object_id = self.objects._collection.save(self.to_mongo())
|
||||
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
|
||||
def drop_collection(cls):
|
||||
"""Drops the entire collection associated with this Document type from
|
||||
the database.
|
||||
"""Drops the entire collection associated with this
|
||||
:class:`~mongoengine.Document` type from the database.
|
||||
"""
|
||||
db = _get_db()
|
||||
db.drop_collection(cls._meta['collection'])
|
||||
|
||||
@@ -87,7 +87,7 @@ class DateTimeField(BaseField):
|
||||
|
||||
class EmbeddedDocumentField(BaseField):
|
||||
"""An embedded document field. Only valid values are subclasses of
|
||||
EmbeddedDocument.
|
||||
:class:`~mongoengine.EmbeddedDocument`.
|
||||
"""
|
||||
|
||||
def __init__(self, document, **kwargs):
|
||||
@@ -179,19 +179,19 @@ class ReferenceField(BaseField):
|
||||
|
||||
def to_mongo(self, document):
|
||||
if isinstance(document, (str, unicode, pymongo.objectid.ObjectId)):
|
||||
_id = document
|
||||
id_ = document
|
||||
else:
|
||||
try:
|
||||
_id = document._id
|
||||
id_ = document.id
|
||||
except:
|
||||
raise ValidationError('You can only reference documents once '
|
||||
'they have been saved to the database')
|
||||
|
||||
if not isinstance(_id, pymongo.objectid.ObjectId):
|
||||
_id = pymongo.objectid.ObjectId(_id)
|
||||
if not isinstance(id_, pymongo.objectid.ObjectId):
|
||||
id_ = pymongo.objectid.ObjectId(id_)
|
||||
|
||||
collection = self.document_type._meta['collection']
|
||||
return pymongo.dbref.DBRef(collection, _id)
|
||||
return pymongo.dbref.DBRef(collection, id_)
|
||||
|
||||
def validate(self, value):
|
||||
assert(isinstance(value, (self.document_type, pymongo.dbref.DBRef)))
|
||||
|
||||
@@ -5,15 +5,19 @@ import pymongo
|
||||
|
||||
class QuerySet(object):
|
||||
"""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):
|
||||
self._document = document
|
||||
self._collection = collection
|
||||
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._ordering = []
|
||||
|
||||
def ensure_index(self, key_or_list, direction=None):
|
||||
"""Ensure that the given indexes are in place.
|
||||
@@ -29,7 +33,8 @@ class QuerySet(object):
|
||||
return self
|
||||
|
||||
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))
|
||||
return self
|
||||
@@ -73,7 +78,7 @@ class QuerySet(object):
|
||||
return result
|
||||
|
||||
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):
|
||||
object_id = pymongo.objectid.ObjectId(object_id)
|
||||
@@ -84,7 +89,7 @@ class QuerySet(object):
|
||||
return result
|
||||
|
||||
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())
|
||||
|
||||
@@ -94,41 +99,54 @@ class QuerySet(object):
|
||||
return self._cursor.count()
|
||||
|
||||
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)
|
||||
# Return self to allow chaining
|
||||
return self
|
||||
|
||||
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)
|
||||
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:
|
||||
self._ordering.append(sort_rule)
|
||||
self._cursor.sort(self._ordering)
|
||||
def __getitem__(self, key):
|
||||
"""Support skip and limit using getitem and slicing syntax.
|
||||
"""
|
||||
# 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
|
||||
|
||||
def explain(self, format=False):
|
||||
"""Return an explain plan record for the
|
||||
:class:`~mongoengine.queryset.QuerySet`\ 's cursor.
|
||||
"""
|
||||
|
||||
plan = self._cursor.explain()
|
||||
if format:
|
||||
import pprint
|
||||
|
||||
Reference in New Issue
Block a user