diff --git a/mongoengine/base/fields.py b/mongoengine/base/fields.py index 6c1bf4f1..a32544d8 100644 --- a/mongoengine/base/fields.py +++ b/mongoengine/base/fields.py @@ -314,11 +314,16 @@ class ComplexBaseField(BaseField): if hasattr(value, 'to_python'): return value.to_python() + BaseDocument = _import_class('BaseDocument') + if isinstance(value, BaseDocument): + # Something is wrong, return the value as it is + return value + is_list = False if not hasattr(value, 'items'): try: is_list = True - value = {k: v for k, v in enumerate(value)} + value = {idx: v for idx, v in enumerate(value)} except TypeError: # Not iterable return the value return value diff --git a/tests/fields/fields.py b/tests/fields/fields.py index 13f3aa84..b375822e 100644 --- a/tests/fields/fields.py +++ b/tests/fields/fields.py @@ -3743,7 +3743,7 @@ class FieldTest(MongoDBTestCase): assert isinstance(doc.field, ToEmbedChild) assert doc.field == to_embed_child - def test_invalid_dict_value(self): + def test_dict_field_invalid_dict_value(self): class DictFieldTest(Document): dictionary = DictField(required=True) @@ -3757,6 +3757,22 @@ class FieldTest(MongoDBTestCase): test.dictionary # Just access to test getter self.assertRaises(ValidationError, test.validate) + def test_dict_field_raises_validation_error_if_wrongly_assign_embedded_doc(self): + class DictFieldTest(Document): + dictionary = DictField(required=True) + + DictFieldTest.drop_collection() + + class Embedded(EmbeddedDocument): + name = StringField() + + embed = Embedded(name='garbage') + doc = DictFieldTest(dictionary=embed) + with self.assertRaises(ValidationError) as ctx_err: + doc.validate() + self.assertIn("'dictionary'", str(ctx_err.exception)) + self.assertIn('Only dictionaries may be used in a DictField', str(ctx_err.exception)) + def test_cls_field(self): class Animal(Document): meta = {'allow_inheritance': True} @@ -3855,6 +3871,28 @@ class EmbeddedDocumentListFieldTestCase(MongoDBTestCase): self.Comments(author='user3', message='message1') ]).save() + def test_fails_upon_validate_if_provide_a_doc_instead_of_a_list_of_doc(self): + # Relates to Issue #1464 + comment = self.Comments(author='John') + + class Title(Document): + content = StringField() + + # Test with an embeddedDocument instead of a list(embeddedDocument) + # It's an edge case but it used to fail with a vague error, making it difficult to troubleshoot it + post = self.BlogPost(comments=comment) + with self.assertRaises(ValidationError) as ctx_err: + post.validate() + self.assertIn("'comments'", str(ctx_err.exception)) + self.assertIn('Only lists and tuples may be used in a list field', str(ctx_err.exception)) + + # Test with a Document + post = self.BlogPost(comments=Title(content='garbage')) + with self.assertRaises(ValidationError) as e: + post.validate() + self.assertIn("'comments'", str(ctx_err.exception)) + self.assertIn('Only lists and tuples may be used in a list field', str(ctx_err.exception)) + def test_no_keyword_filter(self): """ Tests the filter method of a List of Embedded Documents