In my project, all pydantic models inherit from a custom "base model" called GeneralModel.
This enables to configure the same behavior for the entire project in one place.
Let's assume the following implementation:
from pydantic import BaseModel
class GeneralModel(BaseModel):
class Config:
use_enum_values = True
exclude_none = True
One particularly desired behavior is to perform a validation on all fields of a specific type.
Any ideas how to achieve this using my GeneralModel? Different approaches are blessed as well.
Quote from the Pydantic validators documentation:
a single validator can also be called on all fields by passing the special value
'*'
and:
you can also add any subset of the following arguments to the signature (the names must match):
[...]
field: the field being validated. Type of object ispydantic.fields.ModelField.
So you can write a catch-all validator and pass it the ModleField instance as an argument. Then check the type of the field and apply whatever validation you want.
Very simple example:
from typing import Any
from pydantic import BaseModel, validator
from pydantic.fields import ModelField
class GeneralModel(BaseModel):
@validator("*")
def ensure_non_negative(cls, v: Any, field: ModelField) -> Any:
if field.type_ is float and v < 0:
raise ValueError(f"`{field.name}` value must not be negative")
return v
class Config:
...
Usage:
from pydantic import ValidationError
# ... import GeneralModel
class ConcreteModel(GeneralModel):
x: float
y: str = "foo"
print(ConcreteModel(x=3.14))
try:
ConcreteModel(x=-1)
except ValidationError as err:
print(err.json(indent=4))
Output:
x=3.14 y='foo'
[
{
"loc": [
"x"
],
"msg": "`x` value must not be negative",
"type": "value_error"
}
]
Note: When defining a field as for example list[str], Pydantic internally considers the field type to be str, but its shape to be pydantic.fields.SHAPE_LIST. Luckily, shape is also a public attribute of ModelField. See this answer for an example, where this is important.
One of the options is to use Annotated Validators
Then you can perform a validation on the specific type as you request
on all fields of a specific type
So in your case it would be something like this
def my_validator(v: Any) -> Any:
assert v > 0, f'{v} should not be negative'
return v
MyFloat = Annotated[float, AfterValidator(my_validator)]
class ConcreteModel(GeneralModel):
x: MyFloat
y: str = "foo"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With