diff --git a/docs/changelog.rst b/docs/changelog.rst index a2ccb43ce..7514f006a 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -9,6 +9,7 @@ Development - (Fill this out as you fix issues and develop your features). - Fix for uuidRepresentation not read when provided in URI #2741 - Add tests against MongoDB 6.0 and MongoDB 7.0 in the pipeline +- Fix validate() not being called when inheritance is used in EmbeddedDocument and validate is overriden #2784 Changes in 0.27.0 ================= diff --git a/mongoengine/fields.py b/mongoengine/fields.py index a3f5525cc..40469bfce 100644 --- a/mongoengine/fields.py +++ b/mongoengine/fields.py @@ -761,7 +761,7 @@ def validate(self, value, clean=True): "Invalid embedded document instance provided to an " "EmbeddedDocumentField" ) - self.document_type.validate(value, clean) + value.validate(clean=clean) def lookup_member(self, member_name): doc_and_subclasses = [self.document_type] + self.document_type.__subclasses__() diff --git a/tests/fields/test_embedded_document_field.py b/tests/fields/test_embedded_document_field.py index 3b555cc69..fefee4efd 100644 --- a/tests/fields/test_embedded_document_field.py +++ b/tests/fields/test_embedded_document_field.py @@ -7,6 +7,7 @@ Document, EmbeddedDocument, EmbeddedDocumentField, + EmbeddedDocumentListField, GenericEmbeddedDocumentField, IntField, InvalidQueryError, @@ -61,6 +62,48 @@ class MyFailingDoc(Document): class MyFailingdoc2(Document): emb = EmbeddedDocumentField("MyDoc") + def test_embedded_document_field_validate_subclass(self): + class BaseItem(EmbeddedDocument): + f = IntField() + + meta = {"allow_inheritance": True} + + def validate(self, clean=True): + if self.f == 0: + raise Exception("can not be 0") + return super().validate(clean) + + class RealItem(BaseItem): + a = IntField() + + def validate(self, clean=True): + if self.f == 1: + raise Exception("can not be 1") + return super().validate(clean) + + class TopLevel(Document): + item = EmbeddedDocumentField(document_type=BaseItem) + items = EmbeddedDocumentListField(document_type=BaseItem) + + passing_item = RealItem(f=2, a=0) + item = TopLevel(item=passing_item, items=[passing_item]) + item.validate() + + failing_item = RealItem(f=1, a=0) + item = TopLevel(item=failing_item) + with pytest.raises(Exception, match="can not be 1"): + item.validate() + + item = TopLevel(items=[failing_item]) + with pytest.raises(Exception, match="can not be 1"): + item.validate() + + # verify that super calls the parent + failing_item_in_base = RealItem(f=0, a=0) + item = TopLevel(item=failing_item_in_base) + with pytest.raises(Exception, match="can not be 0"): + item.validate() + def test_query_embedded_document_attribute(self): class AdminSettings(EmbeddedDocument): foo1 = StringField()