Added ListField type with unit tests
This commit is contained in:
		| @@ -44,7 +44,7 @@ class BaseField(object): | ||||
|             try: | ||||
|                 value = self._to_python(value) | ||||
|                 self._validate(value) | ||||
|             except (ValueError, AttributeError): | ||||
|             except (ValueError, AttributeError, AssertionError): | ||||
|                 raise ValidationError('Invalid value for field of type "' + | ||||
|                                       self.__class__.__name__ + '"') | ||||
|         elif self.required: | ||||
|   | ||||
| @@ -5,7 +5,7 @@ import re | ||||
| import pymongo | ||||
|  | ||||
|  | ||||
| __all__ = ['StringField', 'IntField', 'EmbeddedDocumentField',  | ||||
| __all__ = ['StringField', 'IntField', 'EmbeddedDocumentField', 'ListField', | ||||
|            'ObjectIdField', 'ValidationError'] | ||||
|  | ||||
|  | ||||
| @@ -17,8 +17,14 @@ class StringField(BaseField): | ||||
|         self.regex = re.compile(regex) if regex else None | ||||
|         self.max_length = max_length | ||||
|         super(StringField, self).__init__(**kwargs) | ||||
|      | ||||
|     def _to_python(self, value): | ||||
|         assert(isinstance(value, (str, unicode))) | ||||
|         return unicode(value) | ||||
|  | ||||
|     def _validate(self, value): | ||||
|         assert(isinstance(value, (str, unicode))) | ||||
|  | ||||
|         if self.max_length is not None and len(value) > self.max_length: | ||||
|             raise ValidationError('String value is too long') | ||||
|  | ||||
| @@ -36,9 +42,12 @@ class IntField(BaseField): | ||||
|         super(IntField, self).__init__(**kwargs) | ||||
|      | ||||
|     def _to_python(self, value): | ||||
|         assert(isinstance(value, int)) | ||||
|         return int(value) | ||||
|  | ||||
|     def _validate(self, value): | ||||
|         assert(isinstance(value, int)) | ||||
|  | ||||
|         if self.min_value is not None and value < self.min_value: | ||||
|             raise ValidationError('Integer value is too small') | ||||
|  | ||||
| @@ -74,3 +83,41 @@ class EmbeddedDocumentField(BaseField): | ||||
|         if not isinstance(value, self.document): | ||||
|             raise ValidationError('Invalid embedded document instance ' | ||||
|                                   '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'}) | ||||
|         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): | ||||
|         """Ensure that a document with an embedded document field may be  | ||||
|         saved in the database. | ||||
|   | ||||
| @@ -56,6 +56,8 @@ class FieldTest(unittest.TestCase): | ||||
|             userid = StringField(r'[0-9a-z_]+$') | ||||
|  | ||||
|         person = Person() | ||||
|         self.assertRaises(ValidationError, person.__setattr__, 'name', 34) | ||||
|  | ||||
|         # Test regex validation on userid | ||||
|         self.assertRaises(ValidationError, person.__setattr__, 'userid', | ||||
|                           'test.User') | ||||
| @@ -80,6 +82,29 @@ class FieldTest(unittest.TestCase): | ||||
|         self.assertRaises(ValidationError, person.__setattr__, 'age', 120) | ||||
|         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): | ||||
|         """Ensure that invalid embedded documents cannot be assigned to | ||||
|         embedded document fields. | ||||
|   | ||||
		Reference in New Issue
	
	Block a user