Added ListField type with unit tests
This commit is contained in:
		| @@ -44,7 +44,7 @@ class BaseField(object): | |||||||
|             try: |             try: | ||||||
|                 value = self._to_python(value) |                 value = self._to_python(value) | ||||||
|                 self._validate(value) |                 self._validate(value) | ||||||
|             except (ValueError, AttributeError): |             except (ValueError, AttributeError, AssertionError): | ||||||
|                 raise ValidationError('Invalid value for field of type "' + |                 raise ValidationError('Invalid value for field of type "' + | ||||||
|                                       self.__class__.__name__ + '"') |                                       self.__class__.__name__ + '"') | ||||||
|         elif self.required: |         elif self.required: | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ import re | |||||||
| import pymongo | import pymongo | ||||||
|  |  | ||||||
|  |  | ||||||
| __all__ = ['StringField', 'IntField', 'EmbeddedDocumentField',  | __all__ = ['StringField', 'IntField', 'EmbeddedDocumentField', 'ListField', | ||||||
|            'ObjectIdField', 'ValidationError'] |            'ObjectIdField', 'ValidationError'] | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -17,8 +17,14 @@ class StringField(BaseField): | |||||||
|         self.regex = re.compile(regex) if regex else None |         self.regex = re.compile(regex) if regex else None | ||||||
|         self.max_length = max_length |         self.max_length = max_length | ||||||
|         super(StringField, self).__init__(**kwargs) |         super(StringField, self).__init__(**kwargs) | ||||||
|  |      | ||||||
|  |     def _to_python(self, value): | ||||||
|  |         assert(isinstance(value, (str, unicode))) | ||||||
|  |         return unicode(value) | ||||||
|  |  | ||||||
|     def _validate(self, value): |     def _validate(self, value): | ||||||
|  |         assert(isinstance(value, (str, unicode))) | ||||||
|  |  | ||||||
|         if self.max_length is not None and len(value) > self.max_length: |         if self.max_length is not None and len(value) > self.max_length: | ||||||
|             raise ValidationError('String value is too long') |             raise ValidationError('String value is too long') | ||||||
|  |  | ||||||
| @@ -36,9 +42,12 @@ class IntField(BaseField): | |||||||
|         super(IntField, self).__init__(**kwargs) |         super(IntField, self).__init__(**kwargs) | ||||||
|      |      | ||||||
|     def _to_python(self, value): |     def _to_python(self, value): | ||||||
|  |         assert(isinstance(value, int)) | ||||||
|         return int(value) |         return int(value) | ||||||
|  |  | ||||||
|     def _validate(self, value): |     def _validate(self, value): | ||||||
|  |         assert(isinstance(value, int)) | ||||||
|  |  | ||||||
|         if self.min_value is not None and value < self.min_value: |         if self.min_value is not None and value < self.min_value: | ||||||
|             raise ValidationError('Integer value is too small') |             raise ValidationError('Integer value is too small') | ||||||
|  |  | ||||||
| @@ -74,3 +83,41 @@ class EmbeddedDocumentField(BaseField): | |||||||
|         if not isinstance(value, self.document): |         if not isinstance(value, self.document): | ||||||
|             raise ValidationError('Invalid embedded document instance ' |             raise ValidationError('Invalid embedded document instance ' | ||||||
|                                   'provided to an EmbeddedDocumentField') |                                   'provided to an EmbeddedDocumentField') | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class ListField(BaseField): | ||||||
|  |     """A list field that wraps a standard field, allowing multiple instances | ||||||
|  |     of the field to be used as a list in the database. | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     def __init__(self, field, **kwargs): | ||||||
|  |         if not isinstance(field, BaseField): | ||||||
|  |             raise ValidationError('Argument to ListField constructor must be ' | ||||||
|  |                                   'a valid field') | ||||||
|  |         self.field = field | ||||||
|  |         super(ListField, self).__init__(**kwargs) | ||||||
|  |  | ||||||
|  |     def _to_python(self, value): | ||||||
|  |         assert(isinstance(value, (list, tuple))) | ||||||
|  |         return list(value) | ||||||
|  |  | ||||||
|  |     def _to_mongo(self, value): | ||||||
|  |         return [self.field._to_mongo(item) for item in value] | ||||||
|  |  | ||||||
|  |     def _validate(self, value): | ||||||
|  |         """Make sure that a list of valid fields is being used. | ||||||
|  |         """ | ||||||
|  | #        print | ||||||
|  | #        print value | ||||||
|  | #        print type(value) | ||||||
|  | #        print isinstance(value, list) | ||||||
|  | #        print | ||||||
|  |         if not isinstance(value, (list, tuple)): | ||||||
|  |             raise ValidationError('Only lists and tuples may be used in a ' | ||||||
|  |                                   'list field') | ||||||
|  |  | ||||||
|  |         try: | ||||||
|  |             [self.field._validate(item) for item in value] | ||||||
|  |         except: | ||||||
|  |             raise ValidationError('All items in a list field must be of the ' | ||||||
|  |                                   'specified type') | ||||||
|   | |||||||
| @@ -111,6 +111,29 @@ class DocumentTest(unittest.TestCase): | |||||||
|         person_obj = collection.find_one({'name': 'Test User'}) |         person_obj = collection.find_one({'name': 'Test User'}) | ||||||
|         self.assertEqual(str(person_obj['_id']), '497ce96f395f2f052a494fd4') |         self.assertEqual(str(person_obj['_id']), '497ce96f395f2f052a494fd4') | ||||||
|  |  | ||||||
|  |     def test_save_list(self): | ||||||
|  |         """Ensure that a list field may be properly saved. | ||||||
|  |         """ | ||||||
|  |         class Comment(EmbeddedDocument): | ||||||
|  |             content = StringField() | ||||||
|  |  | ||||||
|  |         class BlogPost(Document): | ||||||
|  |             content = StringField() | ||||||
|  |             comments = ListField(EmbeddedDocumentField(Comment)) | ||||||
|  |             tags = ListField(StringField()) | ||||||
|  |  | ||||||
|  |         post = BlogPost(content='Went for a walk today...') | ||||||
|  |         post.tags = tags = ['fun', 'leisure'] | ||||||
|  |         comments = [Comment(content='Good for you'), Comment(content='Yay.')] | ||||||
|  |         post.comments = comments | ||||||
|  |         post.save() | ||||||
|  |  | ||||||
|  |         collection = self.db[BlogPost._meta['collection']] | ||||||
|  |         post_obj = collection.find_one() | ||||||
|  |         self.assertEqual(post_obj['tags'], tags) | ||||||
|  |         for comment_obj, comment in zip(post_obj['comments'], comments): | ||||||
|  |             self.assertEqual(comment_obj['content'], comment['content']) | ||||||
|  |  | ||||||
|     def test_save_embedded_document(self): |     def test_save_embedded_document(self): | ||||||
|         """Ensure that a document with an embedded document field may be  |         """Ensure that a document with an embedded document field may be  | ||||||
|         saved in the database. |         saved in the database. | ||||||
|   | |||||||
| @@ -56,6 +56,8 @@ class FieldTest(unittest.TestCase): | |||||||
|             userid = StringField(r'[0-9a-z_]+$') |             userid = StringField(r'[0-9a-z_]+$') | ||||||
|  |  | ||||||
|         person = Person() |         person = Person() | ||||||
|  |         self.assertRaises(ValidationError, person.__setattr__, 'name', 34) | ||||||
|  |  | ||||||
|         # Test regex validation on userid |         # Test regex validation on userid | ||||||
|         self.assertRaises(ValidationError, person.__setattr__, 'userid', |         self.assertRaises(ValidationError, person.__setattr__, 'userid', | ||||||
|                           'test.User') |                           'test.User') | ||||||
| @@ -80,6 +82,29 @@ class FieldTest(unittest.TestCase): | |||||||
|         self.assertRaises(ValidationError, person.__setattr__, 'age', 120) |         self.assertRaises(ValidationError, person.__setattr__, 'age', 120) | ||||||
|         self.assertRaises(ValidationError, person.__setattr__, 'age', 'ten') |         self.assertRaises(ValidationError, person.__setattr__, 'age', 'ten') | ||||||
|  |  | ||||||
|  |     def test_list_validation(self): | ||||||
|  |         """Ensure that a list field only accepts lists with valid elements. | ||||||
|  |         """ | ||||||
|  |         class Comment(EmbeddedDocument): | ||||||
|  |             content = StringField() | ||||||
|  |  | ||||||
|  |         class BlogPost(Document): | ||||||
|  |             content = StringField() | ||||||
|  |             comments = ListField(EmbeddedDocumentField(Comment)) | ||||||
|  |             tags = ListField(StringField()) | ||||||
|  |  | ||||||
|  |         post = BlogPost(content='Went for a walk today...') | ||||||
|  |         self.assertRaises(ValidationError, post.__setattr__, 'tags', 'fun') | ||||||
|  |         self.assertRaises(ValidationError, post.__setattr__, 'tags', [1, 2]) | ||||||
|  |         post.tags = ['fun', 'leisure'] | ||||||
|  |         post.tags = ('fun', 'leisure') | ||||||
|  |  | ||||||
|  |         comments = [Comment(content='Good for you'), Comment(content='Yay.')] | ||||||
|  |         self.assertRaises(ValidationError, post.__setattr__, 'comments', ['a']) | ||||||
|  |         self.assertRaises(ValidationError, post.__setattr__, 'comments', 'Yay') | ||||||
|  |         self.assertRaises(ValidationError, post.__setattr__, 'comments', 'Yay') | ||||||
|  |         post.comments = comments | ||||||
|  |  | ||||||
|     def test_embedded_document_validation(self): |     def test_embedded_document_validation(self): | ||||||
|         """Ensure that invalid embedded documents cannot be assigned to |         """Ensure that invalid embedded documents cannot be assigned to | ||||||
|         embedded document fields. |         embedded document fields. | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user