Reputation: 2899
How can I, in a pydantic type validator, get the actual model? The idea is to read metadata from the Field()
parameter.
This is what I end up with. I tried a WrapValidator
as well, but it does not help, and the model does not appear in the context.
from typing import Annotated, Any
from pydantic import BaseModel, BeforeValidator, Field, ValidationInfo
def use_default_if_none[T](f: T | None, info: ValidationInfo) -> T | Any:
if f is None:
return 42 # QUESTION how to get the model here?
else:
return f
type IntDefault = Annotated[int, BeforeValidator(use_default_if_none)]
class Data(BaseModel):
a: IntDefault = Field(default=42)
print(Data(a=None))
Upvotes: 0
Views: 751
Reputation: 1638
I do not think this is possible using annotated validators. They are explicitly independent of the model class they are used in. They cannot know, in which model they are used, if that makes sense.
To make this work you need validators, that are attached to the model class. One possible solution is to use a field_validator
:
from pydantic import BaseModel, field_validator
class Data(BaseModel):
a: IntDefault = Field(default=42)
@field_validator("a", mode="before")
@classmethod
def validate(cls, value):
if value is None:
field = cls.model_fields["a"]
return field.default
return value
print(Data(a=None))
Which prints:
a=42
If you really want to achieve something along the lines of what you proposed, you need a far more advanced pattern using parametric types (by implementing __class_item__
) to tell IntDefault
, which model it is part of. See for example:
from __future__ import annotations
from typing import Annotated
from pydantic import BaseModel, BeforeValidator, Field
class IntDefault:
def __class_getitem__(cls, item):
def use_default_if_none(value, info):
if value is None:
return item.model_fields[info.field_name].default
return value
return Annotated[int, BeforeValidator(use_default_if_none)]
class Data(BaseModel):
a: IntDefault[Data] = Field(default=42)
print(Data(a=None))
But if possible I would really try to avoid it. I think the pattern based on the parametric type is too advanced and the simpler repetitive pattern I proposed above might be the better choice.
I hope this helps!
Upvotes: 1