Pydantic: Register validators for arbitrary classes

Created on 2 May 2019  路  3Comments  路  Source: samuelcolvin/pydantic

Feature Request

I've got a need to "register" (?) arbitrary validators for custom classes. Right now I can achieve the functionality I want doing something like this:

from typing import Any
import pydantic.validators
from pydantic import BaseModel

class Foo:
    pass

def foo_validator(v: Any) -> Foo:
    if isinstance(v, Foo):
        return v
    if v == "Foo":
        return Foo()
    raise ValueError("Must be a Foo or the string 'Foo'")

pydantic.validators._VALIDATORS.append((Foo, [foo_validator]))

class Bar(BaseModel):
    foo: Foo

bar = Bar(foo="Foo")

This feels a little hacky.
So:
1) Is there already a better way to do something like this that I'm not seeing?
2) Would it be possible to support something less hacky than this? e.g.

pydantic.register_custom_validator((Foo, [foo_validator]))

And then pydantic.validators.find_validators could do something like this:

    for val_type, validators in _VALIDATORS + _CUSTOM_VALIDATORS:
        try:
            if issubclass(type_, val_type):
                return validators
        except TypeError as e:
            raise RuntimeError(f'error checking inheritance of {type_!r} (type: {display_as_type(type_)})') from e

Happy to start working on PR if this seems feasible/reasonable/etc.

question

All 3 comments

Best to use __get_validators__ on Foo like this.

Then you can setup Foo to validate as you wish.

Thanks!

For completeness, here's how to implement the above:

from typing import Any
from pydantic import BaseModel

class Foo:
    @classmethod
    def __get_validators__(cls):
        def validator(v: Any) -> "Foo":
            if isinstance(v, cls):
                return v
            if v == "Foo":
                return cls()
            raise ValueError("Must be a Foo or the string 'Foo'")
        yield validator

class Bar(BaseModel):
    foo: Foo

bar = Bar(foo="Foo")

that will work, but generally more readable if you put the validator method as a classmethod on the Foo class as we do on all the pydantic validator classes, eg. UrlStr.

Was this page helpful?
0 / 5 - 0 ratings