Merge branch 'master' of git://github.com/hmarr/mongoengine
This commit is contained in:
		
							
								
								
									
										3
									
								
								AUTHORS
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								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. | ||||||
|         """ |         """ | ||||||
|  |         try: | ||||||
|             if self._limit == 0: |             if self._limit == 0: | ||||||
|                 raise StopIteration |                 raise StopIteration | ||||||
|             return self._document._from_son(self._cursor.next()) |             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. | ||||||
|         """ |         """ | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user