Merge pull request #848 from seglberg/choice-subclasses
Field Choices Now Accept Subclasses of Documents
This commit is contained in:
		
							
								
								
									
										2
									
								
								AUTHORS
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								AUTHORS
									
									
									
									
									
								
							| @@ -215,4 +215,4 @@ that much better: | |||||||
|  * André Ericson https://github.com/aericson) |  * André Ericson https://github.com/aericson) | ||||||
|  * Mikhail Moshnogorsky (https://github.com/mikhailmoshnogorsky) |  * Mikhail Moshnogorsky (https://github.com/mikhailmoshnogorsky) | ||||||
|  * Diego Berrocal (https://github.com/cestdiego) |  * Diego Berrocal (https://github.com/cestdiego) | ||||||
|  * Matthew Ellison (https://github.com/mmelliso) |  * Matthew Ellison (https://github.com/seglberg) | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ Changelog | |||||||
|  |  | ||||||
| Changes in 0.9.X - DEV | Changes in 0.9.X - DEV | ||||||
| ====================== | ====================== | ||||||
|  | - Field Choices Now Accept Subclasses of Documents | ||||||
| - Ensure Indexes before Each Save #812 | - Ensure Indexes before Each Save #812 | ||||||
| - Generate Unique Indices for Lists of EmbeddedDocuments #358 | - Generate Unique Indices for Lists of EmbeddedDocuments #358 | ||||||
| - Sparse fields #515 | - Sparse fields #515 | ||||||
|   | |||||||
| @@ -158,21 +158,23 @@ class BaseField(object): | |||||||
|     def _validate(self, value, **kwargs): |     def _validate(self, value, **kwargs): | ||||||
|         Document = _import_class('Document') |         Document = _import_class('Document') | ||||||
|         EmbeddedDocument = _import_class('EmbeddedDocument') |         EmbeddedDocument = _import_class('EmbeddedDocument') | ||||||
|         # check choices |  | ||||||
|  |         # Check the Choices Constraint | ||||||
|         if self.choices: |         if self.choices: | ||||||
|             is_cls = isinstance(value, (Document, EmbeddedDocument)) |  | ||||||
|             value_to_check = value.__class__ if is_cls else value |             choice_list = self.choices | ||||||
|             err_msg = 'an instance' if is_cls else 'one' |  | ||||||
|             if isinstance(self.choices[0], (list, tuple)): |             if isinstance(self.choices[0], (list, tuple)): | ||||||
|                 option_keys = [k for k, v in self.choices] |                 choice_list = [k for k, v in self.choices] | ||||||
|                 if value_to_check not in option_keys: |  | ||||||
|                     msg = ('Value must be %s of %s' % |             # Choices which are other types of Documents | ||||||
|                            (err_msg, unicode(option_keys))) |             if isinstance(value, (Document, EmbeddedDocument)): | ||||||
|                     self.error(msg) |                 if not any(isinstance(value, c) for c in choice_list): | ||||||
|             elif value_to_check not in self.choices: |                     self.error( | ||||||
|                 msg = ('Value must be %s of %s' % |                         'Value must be instance of %s' % unicode(choice_list) | ||||||
|                        (err_msg, unicode(self.choices))) |                     ) | ||||||
|                 self.error(msg) |             # Choices which are types other than Documents | ||||||
|  |             elif value not in choice_list: | ||||||
|  |                 self.error('Value must be one of %s' % unicode(choice_list)) | ||||||
|  |  | ||||||
|         # check validation argument |         # check validation argument | ||||||
|         if self.validation is not None: |         if self.validation is not None: | ||||||
|   | |||||||
| @@ -2446,6 +2446,79 @@ class FieldTest(unittest.TestCase): | |||||||
|  |  | ||||||
|         Shirt.drop_collection() |         Shirt.drop_collection() | ||||||
|  |  | ||||||
|  |     def test_choices_validation_documents(self): | ||||||
|  |         """ | ||||||
|  |         Ensure fields with document choices validate given a valid choice. | ||||||
|  |         """ | ||||||
|  |         class UserComments(EmbeddedDocument): | ||||||
|  |             author = StringField() | ||||||
|  |             message = StringField() | ||||||
|  |  | ||||||
|  |         class BlogPost(Document): | ||||||
|  |             comments = ListField( | ||||||
|  |                 GenericEmbeddedDocumentField(choices=(UserComments,)) | ||||||
|  |             ) | ||||||
|  |  | ||||||
|  |         # Ensure Validation Passes | ||||||
|  |         BlogPost(comments=[ | ||||||
|  |             UserComments(author='user2', message='message2'), | ||||||
|  |         ]).save() | ||||||
|  |  | ||||||
|  |     def test_choices_validation_documents_invalid(self): | ||||||
|  |         """ | ||||||
|  |         Ensure fields with document choices validate given an invalid choice. | ||||||
|  |         This should throw a ValidationError exception. | ||||||
|  |         """ | ||||||
|  |         class UserComments(EmbeddedDocument): | ||||||
|  |             author = StringField() | ||||||
|  |             message = StringField() | ||||||
|  |  | ||||||
|  |         class ModeratorComments(EmbeddedDocument): | ||||||
|  |             author = StringField() | ||||||
|  |             message = StringField() | ||||||
|  |  | ||||||
|  |         class BlogPost(Document): | ||||||
|  |             comments = ListField( | ||||||
|  |                 GenericEmbeddedDocumentField(choices=(UserComments,)) | ||||||
|  |             ) | ||||||
|  |  | ||||||
|  |         # Single Entry Failure | ||||||
|  |         post = BlogPost(comments=[ | ||||||
|  |             ModeratorComments(author='mod1', message='message1'), | ||||||
|  |         ]) | ||||||
|  |         self.assertRaises(ValidationError, post.save) | ||||||
|  |  | ||||||
|  |         # Mixed Entry Failure | ||||||
|  |         post = BlogPost(comments=[ | ||||||
|  |             ModeratorComments(author='mod1', message='message1'), | ||||||
|  |             UserComments(author='user2', message='message2'), | ||||||
|  |         ]) | ||||||
|  |         self.assertRaises(ValidationError, post.save) | ||||||
|  |  | ||||||
|  |     def test_choices_validation_documents_inheritance(self): | ||||||
|  |         """ | ||||||
|  |         Ensure fields with document choices validate given subclass of choice. | ||||||
|  |         """ | ||||||
|  |         class Comments(EmbeddedDocument): | ||||||
|  |             meta = { | ||||||
|  |                 'abstract': True | ||||||
|  |             } | ||||||
|  |             author = StringField() | ||||||
|  |             message = StringField() | ||||||
|  |  | ||||||
|  |         class UserComments(Comments): | ||||||
|  |             pass | ||||||
|  |  | ||||||
|  |         class BlogPost(Document): | ||||||
|  |             comments = ListField( | ||||||
|  |                 GenericEmbeddedDocumentField(choices=(Comments,)) | ||||||
|  |             ) | ||||||
|  |  | ||||||
|  |         # Save Valid EmbeddedDocument Type | ||||||
|  |         BlogPost(comments=[ | ||||||
|  |             UserComments(author='user2', message='message2'), | ||||||
|  |         ]).save() | ||||||
|  |  | ||||||
|     def test_choices_get_field_display(self): |     def test_choices_get_field_display(self): | ||||||
|         """Test dynamic helper for returning the display value of a choices |         """Test dynamic helper for returning the display value of a choices | ||||||
|         field. |         field. | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user