fixed merge conflict in queryset test
This commit is contained in:
@@ -6,7 +6,6 @@ import pymongo
|
||||
class ValidationError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class BaseField(object):
|
||||
"""A base class for fields in a MongoDB document. Instances of this class
|
||||
may be added to subclasses of `Document` to define a document's schema.
|
||||
@@ -77,7 +76,10 @@ class ObjectIdField(BaseField):
|
||||
|
||||
def to_mongo(self, value):
|
||||
if not isinstance(value, pymongo.objectid.ObjectId):
|
||||
return pymongo.objectid.ObjectId(str(value))
|
||||
try:
|
||||
return pymongo.objectid.ObjectId(str(value))
|
||||
except Exception, e:
|
||||
raise ValidationError(e.message)
|
||||
return value
|
||||
|
||||
def prepare_query_value(self, value):
|
||||
@@ -103,6 +105,7 @@ class DocumentMetaclass(type):
|
||||
doc_fields = {}
|
||||
class_name = [name]
|
||||
superclasses = {}
|
||||
simple_class = True
|
||||
for base in bases:
|
||||
# Include all fields present in superclasses
|
||||
if hasattr(base, '_fields'):
|
||||
@@ -111,6 +114,29 @@ class DocumentMetaclass(type):
|
||||
# Get superclasses from superclass
|
||||
superclasses[base._class_name] = base
|
||||
superclasses.update(base._superclasses)
|
||||
|
||||
if hasattr(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
|
||||
|
||||
meta = attrs.get('_meta', attrs.get('meta', {}))
|
||||
|
||||
if 'allow_inheritance' not in meta:
|
||||
meta['allow_inheritance'] = True
|
||||
|
||||
# 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['_class_name'] = '.'.join(reversed(class_name))
|
||||
attrs['_superclasses'] = superclasses
|
||||
|
||||
@@ -143,21 +169,12 @@ class TopLevelDocumentMetaclass(DocumentMetaclass):
|
||||
|
||||
collection = name.lower()
|
||||
|
||||
simple_class = True
|
||||
id_field = None
|
||||
base_indexes = []
|
||||
|
||||
# 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']
|
||||
|
||||
id_field = id_field or base._meta.get('id_field')
|
||||
@@ -165,7 +182,6 @@ class TopLevelDocumentMetaclass(DocumentMetaclass):
|
||||
|
||||
meta = {
|
||||
'collection': collection,
|
||||
'allow_inheritance': True,
|
||||
'max_documents': None,
|
||||
'max_size': None,
|
||||
'ordering': [], # default ordering applied at runtime
|
||||
@@ -175,12 +191,6 @@ class TopLevelDocumentMetaclass(DocumentMetaclass):
|
||||
|
||||
# Apply document-defined meta options
|
||||
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
|
||||
|
||||
# Set up collection manager, needs the class to have fields so use
|
||||
@@ -346,7 +356,7 @@ class BaseDocument(object):
|
||||
|
||||
@classmethod
|
||||
def _from_son(cls, son):
|
||||
"""Create an instance of a Document (subclass) from a PyMongo SOM.
|
||||
"""Create an instance of a Document (subclass) from a PyMongo SON.
|
||||
"""
|
||||
# get the class name from the document, falling back to the given
|
||||
# class if unavailable
|
||||
|
||||
@@ -7,7 +7,6 @@ __all__ = ['ConnectionError', 'connect']
|
||||
_connection_settings = {
|
||||
'host': 'localhost',
|
||||
'port': 27017,
|
||||
'pool_size': 1,
|
||||
}
|
||||
_connection = None
|
||||
|
||||
|
||||
@@ -78,9 +78,9 @@ class Document(BaseDocument):
|
||||
object_id = collection.save(doc, safe=safe)
|
||||
except pymongo.errors.OperationFailure, err:
|
||||
message = 'Could not save document (%s)'
|
||||
if 'duplicate key' in str(err):
|
||||
message = 'Tried to save duplicate unique keys (%s)'
|
||||
raise OperationError(message % str(err))
|
||||
if u'duplicate key' in unicode(err):
|
||||
message = u'Tried to save duplicate unique keys (%s)'
|
||||
raise OperationError(message % unicode(err))
|
||||
id_field = self._meta['id_field']
|
||||
self[id_field] = self._fields[id_field].to_python(object_id)
|
||||
|
||||
@@ -95,7 +95,8 @@ class Document(BaseDocument):
|
||||
try:
|
||||
self.__class__.objects(**{id_field: object_id}).delete(safe=safe)
|
||||
except pymongo.errors.OperationFailure, err:
|
||||
raise OperationError('Could not delete document (%s)' % str(err))
|
||||
message = u'Could not delete document (%s)' % err.message
|
||||
raise OperationError(message)
|
||||
|
||||
def reload(self):
|
||||
"""Reloads all attributes from the database.
|
||||
|
||||
@@ -9,18 +9,8 @@ import decimal
|
||||
|
||||
|
||||
__all__ = ['StringField', 'IntField', 'FloatField', 'BooleanField',
|
||||
'DateTimeField', 'EmbeddedDocumentField', 'ListField',
|
||||
'ObjectIdField', 'ReferenceField', 'ValidationError',
|
||||
'URLField', 'DecimalField']
|
||||
|
||||
|
||||
URL_REGEX = re.compile(
|
||||
r'^https?://'
|
||||
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?|'
|
||||
r'localhost|'
|
||||
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'
|
||||
r'(?::\d+)?'
|
||||
r'(?:/?|[/?]\S+)$', re.IGNORECASE)
|
||||
'DateTimeField', 'EmbeddedDocumentField', 'ListField', 'DictField',
|
||||
'ObjectIdField', 'ReferenceField', 'ValidationError']
|
||||
|
||||
|
||||
class StringField(BaseField):
|
||||
@@ -104,6 +94,8 @@ class FloatField(BaseField):
|
||||
return float(value)
|
||||
|
||||
def validate(self, value):
|
||||
if isinstance(value, int):
|
||||
value = float(value)
|
||||
assert isinstance(value, float)
|
||||
|
||||
if self.min_value is not None and value < self.min_value:
|
||||
@@ -191,6 +183,7 @@ class EmbeddedDocumentField(BaseField):
|
||||
if not isinstance(value, self.document):
|
||||
raise ValidationError('Invalid embedded document instance '
|
||||
'provided to an EmbeddedDocumentField')
|
||||
self.document.validate(value)
|
||||
|
||||
def lookup_member(self, member_name):
|
||||
return self.document._fields.get(member_name)
|
||||
@@ -240,6 +233,28 @@ class ListField(BaseField):
|
||||
return self.field.lookup_member(member_name)
|
||||
|
||||
|
||||
class DictField(BaseField):
|
||||
"""A dictionary field that wraps a standard Python dictionary. This is
|
||||
similar to an embedded document, but the structure is not defined.
|
||||
|
||||
.. versionadded:: 0.2.3
|
||||
"""
|
||||
|
||||
def validate(self, value):
|
||||
"""Make sure that a list of valid fields is being used.
|
||||
"""
|
||||
if not isinstance(value, dict):
|
||||
raise ValidationError('Only dictionaries may be used in a '
|
||||
'DictField')
|
||||
|
||||
if any(('.' in k or '$' in k) for k in value):
|
||||
raise ValidationError('Invalid dictionary key name - keys may not '
|
||||
'contain "." or "$" characters')
|
||||
|
||||
def lookup_member(self, member_name):
|
||||
return BaseField(name=member_name)
|
||||
|
||||
|
||||
class ReferenceField(BaseField):
|
||||
"""A reference to a document that will be automatically dereferenced on
|
||||
access (lazily).
|
||||
@@ -271,20 +286,19 @@ class ReferenceField(BaseField):
|
||||
return super(ReferenceField, self).__get__(instance, owner)
|
||||
|
||||
def to_mongo(self, document):
|
||||
if isinstance(document, (str, unicode, pymongo.objectid.ObjectId)):
|
||||
# document may already be an object id
|
||||
id_ = document
|
||||
else:
|
||||
id_field_name = self.document_type._meta['id_field']
|
||||
id_field = self.document_type._fields[id_field_name]
|
||||
|
||||
if isinstance(document, Document):
|
||||
# We need the id from the saved object to create the DBRef
|
||||
id_ = document.id
|
||||
if id_ is None:
|
||||
raise ValidationError('You can only reference documents once '
|
||||
'they have been saved to the database')
|
||||
else:
|
||||
id_ = document
|
||||
|
||||
# id may be a string rather than an ObjectID object
|
||||
if not isinstance(id_, pymongo.objectid.ObjectId):
|
||||
id_ = pymongo.objectid.ObjectId(id_)
|
||||
|
||||
id_ = id_field.to_mongo(id_)
|
||||
collection = self.document_type._meta['collection']
|
||||
return pymongo.dbref.DBRef(collection, id_)
|
||||
|
||||
|
||||
@@ -11,6 +11,14 @@ __all__ = ['queryset_manager', 'Q', 'InvalidQueryError',
|
||||
REPR_OUTPUT_SIZE = 20
|
||||
|
||||
|
||||
class DoesNotExist(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class MultipleObjectsReturned(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidQueryError(Exception):
|
||||
pass
|
||||
|
||||
@@ -29,14 +37,14 @@ class Q(object):
|
||||
AND = '&&'
|
||||
OPERATORS = {
|
||||
'eq': 'this.%(field)s == %(value)s',
|
||||
'neq': 'this.%(field)s != %(value)s',
|
||||
'ne': 'this.%(field)s != %(value)s',
|
||||
'gt': 'this.%(field)s > %(value)s',
|
||||
'gte': 'this.%(field)s >= %(value)s',
|
||||
'lt': 'this.%(field)s < %(value)s',
|
||||
'lte': 'this.%(field)s <= %(value)s',
|
||||
'lte': 'this.%(field)s <= %(value)s',
|
||||
'in': 'this.%(field)s.indexOf(%(value)s) != -1',
|
||||
'nin': 'this.%(field)s.indexOf(%(value)s) == -1',
|
||||
'in': '%(value)s.indexOf(this.%(field)s) != -1',
|
||||
'nin': '%(value)s.indexOf(this.%(field)s) == -1',
|
||||
'mod': '%(field)s %% %(value)s',
|
||||
'all': ('%(value)s.every(function(a){'
|
||||
'return this.%(field)s.indexOf(a) != -1 })'),
|
||||
@@ -262,7 +270,7 @@ class QuerySet(object):
|
||||
def _transform_query(cls, _doc_cls=None, **query):
|
||||
"""Transform a query from Django-style format to Mongo format.
|
||||
"""
|
||||
operators = ['neq', 'gt', 'gte', 'lt', 'lte', 'in', 'nin', 'mod',
|
||||
operators = ['ne', 'gt', 'gte', 'lt', 'lte', 'in', 'nin', 'mod',
|
||||
'all', 'size', 'exists']
|
||||
|
||||
mongo_query = {}
|
||||
@@ -280,7 +288,7 @@ class QuerySet(object):
|
||||
|
||||
# Convert value to proper value
|
||||
field = fields[-1]
|
||||
if op in (None, 'neq', 'gt', 'gte', 'lt', 'lte'):
|
||||
if op in (None, 'ne', 'gt', 'gte', 'lt', 'lte'):
|
||||
value = field.prepare_query_value(value)
|
||||
elif op in ('in', 'nin', 'all'):
|
||||
# 'in', 'nin' and 'all' require a list of values
|
||||
@@ -297,6 +305,46 @@ class QuerySet(object):
|
||||
|
||||
return mongo_query
|
||||
|
||||
def get(self, *q_objs, **query):
|
||||
"""Retrieve the the matching object raising
|
||||
:class:`~mongoengine.queryset.MultipleObjectsReturned` or
|
||||
:class:`~mongoengine.queryset.DoesNotExist` exceptions if multiple or
|
||||
no results are found.
|
||||
"""
|
||||
self.__call__(*q_objs, **query)
|
||||
count = self.count()
|
||||
if count == 1:
|
||||
return self[0]
|
||||
elif count > 1:
|
||||
message = u'%d items returned, instead of 1' % count
|
||||
raise MultipleObjectsReturned(message)
|
||||
else:
|
||||
raise DoesNotExist('Document not found')
|
||||
|
||||
def get_or_create(self, *q_objs, **query):
|
||||
"""Retreive unique object or create, if it doesn't exist. Raises
|
||||
:class:`~mongoengine.queryset.MultipleObjectsReturned` if multiple
|
||||
results are found. A new document will be created if the document
|
||||
doesn't exists; a dictionary of default values for the new document
|
||||
may be provided as a keyword argument called :attr:`defaults`.
|
||||
"""
|
||||
defaults = query.get('defaults', {})
|
||||
if query.has_key('defaults'):
|
||||
del query['defaults']
|
||||
|
||||
self.__call__(*q_objs, **query)
|
||||
count = self.count()
|
||||
if count == 0:
|
||||
query.update(defaults)
|
||||
doc = self._document(**query)
|
||||
doc.save()
|
||||
return doc
|
||||
elif count == 1:
|
||||
return self.first()
|
||||
else:
|
||||
message = u'%d items returned, instead of 1' % count
|
||||
raise MultipleObjectsReturned(message)
|
||||
|
||||
def first(self):
|
||||
"""Retrieve the first object matching the query.
|
||||
"""
|
||||
@@ -544,9 +592,10 @@ class QuerySet(object):
|
||||
self._collection.update(self._query, update, safe=safe_update,
|
||||
multi=True)
|
||||
except pymongo.errors.OperationFailure, err:
|
||||
if str(err) == 'multi not coded yet':
|
||||
raise OperationError('update() method requires MongoDB 1.1.3+')
|
||||
raise OperationError('Update failed (%s)' % str(err))
|
||||
if unicode(err) == u'multi not coded yet':
|
||||
message = u'update() method requires MongoDB 1.1.3+'
|
||||
raise OperationError(message)
|
||||
raise OperationError(u'Update failed (%s)' % unicode(err))
|
||||
|
||||
def update_one(self, safe_update=True, **update):
|
||||
"""Perform an atomic update on first field matched by the query.
|
||||
@@ -733,15 +782,16 @@ class QuerySetManager(object):
|
||||
# owner is the document that contains the QuerySetManager
|
||||
queryset = QuerySet(owner, self._collection)
|
||||
if self._manager_func:
|
||||
queryset = self._manager_func(queryset)
|
||||
queryset = self._manager_func(owner, queryset)
|
||||
return queryset
|
||||
|
||||
|
||||
def queryset_manager(func):
|
||||
"""Decorator that allows you to define custom QuerySet managers on
|
||||
"""Decorator that allows you to define custom QuerySet managers on
|
||||
:class:`~mongoengine.Document` classes. The manager must be a function that
|
||||
accepts a :class:`~mongoengine.queryset.QuerySet` as its only argument, and
|
||||
returns a :class:`~mongoengine.queryset.QuerySet`, probably the same one
|
||||
but modified in some way.
|
||||
accepts a :class:`~mongoengine.Document` class as its first argument, and a
|
||||
:class:`~mongoengine.queryset.QuerySet` as its second argument. The method
|
||||
function should return a :class:`~mongoengine.queryset.QuerySet`, probably
|
||||
the same one that was passed in, but modified in some way.
|
||||
"""
|
||||
return QuerySetManager(func)
|
||||
|
||||
Reference in New Issue
Block a user