Merge branch 'master' of git://github.com/hmarr/mongoengine
This commit is contained in:
commit
0b62c9d2f6
5
AUTHORS
5
AUTHORS
@ -1,3 +1,4 @@
|
|||||||
Harry Marr <harry@tractiondigital>
|
Harry Marr <harry@hmarr.com>
|
||||||
Matt Dennewitz <mattdennewitz@gmail.com>
|
Matt Dennewitz <mattdennewitz@gmail.com>
|
||||||
Deepak Thukral <iapain@yahoo.com>
|
Deepak Thukral <iapain@yahoo.com>
|
||||||
|
Florian Schlachter <flori@n-schlachter.de>
|
||||||
|
@ -54,6 +54,8 @@ Fields
|
|||||||
|
|
||||||
.. autoclass:: mongoengine.ListField
|
.. autoclass:: mongoengine.ListField
|
||||||
|
|
||||||
|
.. autoclass:: mongoengine.BinaryField
|
||||||
|
|
||||||
.. autoclass:: mongoengine.ObjectIdField
|
.. autoclass:: mongoengine.ObjectIdField
|
||||||
|
|
||||||
.. autoclass:: mongoengine.ReferenceField
|
.. autoclass:: mongoengine.ReferenceField
|
||||||
|
@ -13,6 +13,13 @@ fetch documents from the database::
|
|||||||
for user in User.objects:
|
for user in User.objects:
|
||||||
print user.name
|
print user.name
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
Once the iteration finishes (when :class:`StopIteration` is raised),
|
||||||
|
:meth:`~mongoengine.queryset.QuerySet.rewind` will be called so that the
|
||||||
|
:class:`~mongoengine.queryset.QuerySet` may be iterated over again. The
|
||||||
|
results of the first iteration are *not* cached, so the database will be hit
|
||||||
|
each time the :class:`~mongoengine.queryset.QuerySet` is iterated over.
|
||||||
|
|
||||||
Filtering queries
|
Filtering queries
|
||||||
=================
|
=================
|
||||||
The query may be filtered by calling the
|
The query may be filtered by calling the
|
||||||
|
@ -86,7 +86,8 @@ class ObjectIdField(BaseField):
|
|||||||
try:
|
try:
|
||||||
return pymongo.objectid.ObjectId(str(value))
|
return pymongo.objectid.ObjectId(str(value))
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
raise ValidationError(e.message)
|
#e.message attribute has been deprecated since Python 2.6
|
||||||
|
raise ValidationError(str(e))
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def prepare_query_value(self, op, value):
|
def prepare_query_value(self, op, value):
|
||||||
|
@ -30,6 +30,7 @@ class User(Document):
|
|||||||
is_active = BooleanField(default=True)
|
is_active = BooleanField(default=True)
|
||||||
is_superuser = BooleanField(default=False)
|
is_superuser = BooleanField(default=False)
|
||||||
last_login = DateTimeField(default=datetime.datetime.now)
|
last_login = DateTimeField(default=datetime.datetime.now)
|
||||||
|
date_joined = DateTimeField(default=datetime.datetime.now)
|
||||||
|
|
||||||
def get_full_name(self):
|
def get_full_name(self):
|
||||||
"""Returns the users first and last names, separated by a space.
|
"""Returns the users first and last names, separated by a space.
|
||||||
@ -70,7 +71,20 @@ class User(Document):
|
|||||||
"""Create (and save) a new user with the given username, password and
|
"""Create (and save) a new user with the given username, password and
|
||||||
email address.
|
email address.
|
||||||
"""
|
"""
|
||||||
user = User(username=username, email=email)
|
now = datetime.datetime.now()
|
||||||
|
|
||||||
|
# Normalize the address by lowercasing the domain part of the email
|
||||||
|
# address.
|
||||||
|
# Not sure why we'r allowing null email when its not allowed in django
|
||||||
|
if email is not None:
|
||||||
|
try:
|
||||||
|
email_name, domain_part = email.strip().split('@', 1)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
email = '@'.join([email_name, domain_part.lower()])
|
||||||
|
|
||||||
|
user = User(username=username, email=email, date_joined=now)
|
||||||
user.set_password(password)
|
user.set_password(password)
|
||||||
user.save()
|
user.save()
|
||||||
return user
|
return user
|
||||||
|
@ -11,7 +11,8 @@ import decimal
|
|||||||
__all__ = ['StringField', 'IntField', 'FloatField', 'BooleanField',
|
__all__ = ['StringField', 'IntField', 'FloatField', 'BooleanField',
|
||||||
'DateTimeField', 'EmbeddedDocumentField', 'ListField', 'DictField',
|
'DateTimeField', 'EmbeddedDocumentField', 'ListField', 'DictField',
|
||||||
'ObjectIdField', 'ReferenceField', 'ValidationError',
|
'ObjectIdField', 'ReferenceField', 'ValidationError',
|
||||||
'DecimalField', 'URLField', 'GenericReferenceField']
|
'DecimalField', 'URLField', 'GenericReferenceField',
|
||||||
|
'BinaryField']
|
||||||
|
|
||||||
RECURSIVE_REFERENCE_CONSTANT = 'self'
|
RECURSIVE_REFERENCE_CONSTANT = 'self'
|
||||||
|
|
||||||
@ -442,3 +443,23 @@ class GenericReferenceField(BaseField):
|
|||||||
|
|
||||||
def prepare_query_value(self, op, value):
|
def prepare_query_value(self, op, value):
|
||||||
return self.to_mongo(value)['_ref']
|
return self.to_mongo(value)['_ref']
|
||||||
|
|
||||||
|
class BinaryField(BaseField):
|
||||||
|
"""A binary data field.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, max_bytes=None, **kwargs):
|
||||||
|
self.max_bytes = max_bytes
|
||||||
|
super(BinaryField, self).__init__(**kwargs)
|
||||||
|
|
||||||
|
def to_mongo(self, value):
|
||||||
|
return pymongo.binary.Binary(value)
|
||||||
|
|
||||||
|
def to_python(self, value):
|
||||||
|
return str(value)
|
||||||
|
|
||||||
|
def validate(self, value):
|
||||||
|
assert isinstance(value, str)
|
||||||
|
|
||||||
|
if self.max_bytes is not None and len(value) > self.max_bytes:
|
||||||
|
raise ValidationError('Binary value is too long')
|
||||||
|
@ -418,9 +418,18 @@ class QuerySet(object):
|
|||||||
def next(self):
|
def next(self):
|
||||||
"""Wrap the result in a :class:`~mongoengine.Document` object.
|
"""Wrap the result in a :class:`~mongoengine.Document` object.
|
||||||
"""
|
"""
|
||||||
if self._limit == 0:
|
try:
|
||||||
raise StopIteration
|
if self._limit == 0:
|
||||||
return self._document._from_son(self._cursor.next())
|
raise StopIteration
|
||||||
|
return self._document._from_son(self._cursor.next())
|
||||||
|
except StopIteration, e:
|
||||||
|
self.rewind()
|
||||||
|
raise e
|
||||||
|
|
||||||
|
def rewind(self):
|
||||||
|
"""Rewind the cursor to its unevaluated state.
|
||||||
|
"""
|
||||||
|
self._cursor.rewind()
|
||||||
|
|
||||||
def count(self):
|
def count(self):
|
||||||
"""Count the selected elements in the query.
|
"""Count the selected elements in the query.
|
||||||
|
@ -495,6 +495,61 @@ class FieldTest(unittest.TestCase):
|
|||||||
Post.drop_collection()
|
Post.drop_collection()
|
||||||
User.drop_collection()
|
User.drop_collection()
|
||||||
|
|
||||||
|
def test_binary_fields(self):
|
||||||
|
"""Ensure that binary fields can be stored and retrieved.
|
||||||
|
"""
|
||||||
|
class Attachment(Document):
|
||||||
|
content_type = StringField()
|
||||||
|
blob = BinaryField()
|
||||||
|
|
||||||
|
BLOB = '\xe6\x00\xc4\xff\x07'
|
||||||
|
MIME_TYPE = 'application/octet-stream'
|
||||||
|
|
||||||
|
Attachment.drop_collection()
|
||||||
|
|
||||||
|
attachment = Attachment(content_type=MIME_TYPE, blob=BLOB)
|
||||||
|
attachment.save()
|
||||||
|
|
||||||
|
attachment_1 = Attachment.objects().first()
|
||||||
|
self.assertEqual(MIME_TYPE, attachment_1.content_type)
|
||||||
|
self.assertEqual(BLOB, attachment_1.blob)
|
||||||
|
|
||||||
|
Attachment.drop_collection()
|
||||||
|
|
||||||
|
def test_binary_validation(self):
|
||||||
|
"""Ensure that invalid values cannot be assigned to binary fields.
|
||||||
|
"""
|
||||||
|
class Attachment(Document):
|
||||||
|
blob = BinaryField()
|
||||||
|
|
||||||
|
class AttachmentRequired(Document):
|
||||||
|
blob = BinaryField(required=True)
|
||||||
|
|
||||||
|
class AttachmentSizeLimit(Document):
|
||||||
|
blob = BinaryField(max_bytes=4)
|
||||||
|
|
||||||
|
Attachment.drop_collection()
|
||||||
|
AttachmentRequired.drop_collection()
|
||||||
|
AttachmentSizeLimit.drop_collection()
|
||||||
|
|
||||||
|
attachment = Attachment()
|
||||||
|
attachment.validate()
|
||||||
|
attachment.blob = 2
|
||||||
|
self.assertRaises(ValidationError, attachment.validate)
|
||||||
|
|
||||||
|
attachment_required = AttachmentRequired()
|
||||||
|
self.assertRaises(ValidationError, attachment_required.validate)
|
||||||
|
attachment_required.blob = '\xe6\x00\xc4\xff\x07'
|
||||||
|
attachment_required.validate()
|
||||||
|
|
||||||
|
attachment_size_limit = AttachmentSizeLimit(blob='\xe6\x00\xc4\xff\x07')
|
||||||
|
self.assertRaises(ValidationError, attachment_size_limit.validate)
|
||||||
|
attachment_size_limit.blob = '\xe6\x00\xc4\xff'
|
||||||
|
attachment_size_limit.validate()
|
||||||
|
|
||||||
|
Attachment.drop_collection()
|
||||||
|
AttachmentRequired.drop_collection()
|
||||||
|
AttachmentSizeLimit.drop_collection()
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@ -189,6 +189,18 @@ class QuerySetTest(unittest.TestCase):
|
|||||||
person = self.Person.objects.get(age=50)
|
person = self.Person.objects.get(age=50)
|
||||||
self.assertEqual(person.name, "User C")
|
self.assertEqual(person.name, "User C")
|
||||||
|
|
||||||
|
def test_repeated_iteration(self):
|
||||||
|
"""Ensure that QuerySet rewinds itself one iteration finishes.
|
||||||
|
"""
|
||||||
|
self.Person(name='Person 1').save()
|
||||||
|
self.Person(name='Person 2').save()
|
||||||
|
|
||||||
|
queryset = self.Person.objects
|
||||||
|
people1 = [person for person in queryset]
|
||||||
|
people2 = [person for person in queryset]
|
||||||
|
|
||||||
|
self.assertEqual(people1, people2)
|
||||||
|
|
||||||
def test_regex_query_shortcuts(self):
|
def test_regex_query_shortcuts(self):
|
||||||
"""Ensure that contains, startswith, endswith, etc work.
|
"""Ensure that contains, startswith, endswith, etc work.
|
||||||
"""
|
"""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user