Scille / umongo

sync/async MongoDB ODM, yes.
MIT License
446 stars 63 forks source link

Document level validation #108

Open lafrech opened 7 years ago

lafrech commented 7 years ago

AFAIU, there is no explicit way to declare schema-level validation methods.

If I want to validate a Document at schema leval, I create a validation method and add it to both pre_insert and pre_update.

class MyDoc(Document):

    a = IntField()
    b = IntField()

    def validate_ints(self):
        if a > b:
            raise ValidationError("a should be lesser than or equal to b")

    def pre_update(self):
        self.validate_ints()
        super(MyDoc, self).pre_update()

    def pre_insert(self):
        self.validate_ints()
        super(MyDoc, self).pre_insert()

We could add a validate hook that Documents could override, like pre_insert and all.

class MyDoc(Document):

    a = IntField()
    b = IntField()

    def validate_document(self):
        super(MyDoc, self).validate_document()
        if a > b:
            raise ValidationError("a should be lesser than or equal to b")

Or maybe a decorator a la Marshmallow like @validates_schema to allow the user to register schemas validators. This can look nicer. It allows to register several validation methods in the same class in a nice way, and it avoids bothering with calling super() when subclassing. We lose control on the order of execution but does that really matter?

class MyDoc(Document):

    a = IntField()
    b = IntField()

    @validates_document
    def validate_ints(self):
        if a > b:
            raise ValidationError("a should be lesser than or equal to b")

The fact that this validation happens at commit time is intentional: just like with required, you may build your Document in several steps, so raising a ValidationError at __init__ (load) time would be too restrictive.

I don't see any way to transfer this validation method to the auto-generated Marshmallow schema as it may use any method of the Document while the schema-level validators in Marshmallow only get the input data as a dict. I'm not sure how much of an issue this is in practice.

What do you think?

Or is there an existing mechanism I didn't find, already?

lafrech commented 5 years ago

From the discussions about validation decorators priorities in marshmallow, I think I'd rather stick to a simple hook approach where we keep control on the execution order.

lafrech commented 5 years ago

Also, it would be great if the validation hook was called recursively on embedded documents.

Currently, when I need validation on an EmbeddedDocument, I must call it manually from each Document embedding it which is cumbersome and error-prone.