Reputation: 301
I have a project using fastapi and pydantic. I want to define certain subtypes of standard library types, eg:
def check_negative(v: int) -> int:
assert v >= 0, f"{v} is negative"
return v
PositiveInt = Annotated[int, AfterValidator(check_negative)]
class DemoModel(BaseModel):
numbers: list[PositiveInt]
ISSUE:
The following code runs the evaluation only when the list of numbers is passed on instantiation of the DemoModel. Meaning numbers
contains false values until it is actually used in a BaseModel. This is unconvenient since i want to evaluate the value, for example, directly in the route of a defined fastapi endpoint and not wait until the value is passed down to the model.
numbers = [PositiveInt(2), PositiveInt(-3)] <--- should throw evaluation error here
print(numbers)
demo = DemoModel(numbers=numbers) <--- throws evaluation error here
QUESTION:
Is there a way to run the validations like AfterValidator
directly on the instantiation of the type PositiveInt
and not just when it is used for the instantiation of a BaseModel class?
So that in the given example, the validation error would be thrown in the first line numbers = [PositiveInt(2), PositiveInt(-3)]
and not at the end at demo = DemoModel(numbers=numbers)
.
Thanks for your help!
Upvotes: 0
Views: 2250
Reputation: 36
You could use TypeAdapter
like this:
from pydantic import TypeAdapter
TypeAdapter(list[PositiveInt]).validate_python([2, -3])
Python docs on type adapter: https://docs.pydantic.dev/latest/api/type_adapter/
Upvotes: 2
Reputation: 301
Not sure if this is the best approach, but this is how i got it working now, after reading this part of the pydantic docs:
class PositiveInt(int):
def __new__(cls, number: int):
check_negative(number)
return super().__new__(cls, number)
@classmethod
def __get_pydantic_core_schema__(
cls, source: Type[Any], handler: GetCoreSchemaHandler
) -> core_schema.CoreSchema:
return core_schema.general_after_validator_function(
cls.validate,
core_schema.int_schema(),
serialization=core_schema.int_schema(),
)
@classmethod
def validate(cls, v: int, _info):
return check_negative(v)
class DemoModel(BaseModel):
numbers: list[PositiveInt]
This gives me assertion errors right on the instantiation of PositiveInt
AND i can use it in DemoModel as well.
if __name__ == "__main__":
a = PositiveInt(1)
b = PositiveInt(-3)
c = DemoModel(numbers=[PositiveInt(1), PositiveInt(-3)])
Upvotes: 1