Validators
四种validator
Pydantic提供了四种validator
- BeforeValidator
运行在Pydantic内部的校验转换之前,入参为输入值Any,返回值为Any。 - AfterValidator
运行在Pydantic内部的校验转换之后,入参和返回值为正确的字段类型。 - PlainValidator
运行时间和BeforeValidator相同,但执行完之后整个校验过程结束,不再执行其他validator和Pydantic内部的校验流程。 - WrapValidator
可以运行在pydantic和其他validator之前或者之后,或者返回值、抛出异常立即结束校验流程。
可以使用多个BeforeValidator、AfterValidator和WrapperValidator,但是只能有一个PlainValidator。
关于执行顺序,从右到左执行所有Before和Wrap校验器,再从左到右执行所有After校验器。
以下示例展示了validator的用法。
from typing import Any, List
from pydantic import BaseModel, ValidationError
from pydantic.functional_validators import AfterValidator, BeforeValidator, WrapValidator
from pydantic_core.core_schema import ValidatorFunctionWrapHandler, ValidationInfo
from typing_extensions import Annotated
def check_squares(v: int) -> int:
print('square')
assert v ** 0.5 % 1 == 0, f'{v} is not a square number'
return v
def double(v: Any) -> Any:
print('double')
return v * 2
def maybe_strip_whitespace(
v: Any, handler: ValidatorFunctionWrapHandler, info: ValidationInfo
) -> int:
print('wrap')
if not isinstance(v, int):
v = int(v)
if v >= 22:
try:
# 执行后续校验器链
return handler(v)
except ValidationError as e:
print(e)
return handler(v / 2)
# 直接返回,结束校验
return v
MyNumber = Annotated[int, AfterValidator(check_squares), WrapValidator(maybe_strip_whitespace), BeforeValidator(double)]
class DemoModel(BaseModel):
number: List[MyNumber]
print(DemoModel(number=[8, '2']))
'''
double
wrap
double
wrap
square
1 validation error for ValidatorCallable
Assertion failed, 22 is not a square number [type=assertion_error, input_value=22, input_type=int]
For further information visit https://errors.pydantic.dev/2.1/v/assertion_error
square
1 validation error for DemoModel
number.1
Assertion failed, 11 is not a square number [type=assertion_error, input_value=11.0, input_type=float]
For further information visit https://errors.pydantic.dev/2.1/v/assertion_error
'''
@field_validator
- field_validator可以是Before或者AfterValidator(默认)。
- 被field_validator装饰的方法变成类方法(返回了classmethod),被装饰方法第一个参数是当前Model类,第二个参数为待校验的值,如果有第三个参数,则是pydantic.FieldValidationInfo,校验信息。
- @field_validator可变参数是要校验的字段名,可以是多个,'*'表示所有字段。
- check_fields关键字参数,检验字段是否存在在对象身上。
- 要么抛出ValidationError和AssertionError,要么返回处理过的值。
import json
from typing import Type
from pydantic import BaseModel, field_serializer, field_validator, ValidationError
import builtins
class AppModel(BaseModel):
tp: Type = float
# 序列化float类为字符串'float'
@field_serializer('tp')
def ser_tp(self, value):
return value.__name__
# 反序列化'float'为 class float
@field_validator('tp', mode='before')
def validate_tp(cls, value):
if isinstance(value, type):
return value
if isinstance(value, str):
v = getattr(builtins, value, None)
if v is not None:
return v
raise ValidationError('unknow type: ' + value)
js = AppModel(tp=float).model_dump_json()
print(js)
print(AppModel(**json.loads(js)))
字段校验按照定义的顺序进行,所以在使用ValidationInfo时需要注意访问的字段是否已经被校验处理过,如果要使用多个字段信息进行验证,可以使用@model_validator。
@model_validator
- model_validator可以是mode=‘before’, mode=‘after’ or mode=‘wrap’。
- mode=‘before’,装饰的方法是一个类方法,第一个参数是cls类,第二个参数是Dict[str, Any],即原始输入(如果没有被model='wrap’修改的话),第三个参数(如果有)是ValidationInfo,返回值是Dict[str, Any]。
- mode=‘after’,在validator和pydantic校验完成之后调用,是实例方法,只有一个参数self,此时属性已在实例对象上。
- mode=‘wrap’,类方法,在before之前被调用,第一个参数是cls,第二个参数是原始输入Dict[str, Any],第三个参数是ValidatorCallable校验器链,接受一个参数执行后续校验流程,第四个参数是ValidationInfo。
Special Types
- SkipValidation,跳过验证
- InstanceOf,相当于isincetance(obj, cls)
class Model(BaseModel):
names: List[SkipValidation[str]]
values: List[instanceOf[int]]
Validation Context
运行时动态修改校验行为,文档