在pydantic的数据模型校验场景中,跨字段依赖校验是非常常见的需求,比如用户注册时密码和确认密码需要一致,任务的时间范围中开始时间不能晚于结束时间。很多开发者熟悉用field_validator校验单个字段,却不知道它也能实现跨字段的依赖校验。
field_validator跨字段校验的核心思路
field_validator装饰器默认是对单个字段进行校验,但我们可以通过访问校验方法的info参数获取整个模型的实例数据,从而拿到其他字段的值,实现跨字段的依赖判断。pydantic V2版本中,field_validator的校验方法接收的第一个参数是字段值,第二个是ValidationInfo类型的info对象,info.data属性包含了所有已经校验过的字段的字典数据。
基础跨字段校验示例:密码确认场景
最常见的跨字段校验场景是密码和确认密码的一致性校验,我们可以在确认密码的字段校验器中获取密码字段的值进行对比。
from pydantic import BaseModel, field_validator, ValidationError
class UserRegister(BaseModel):
password: str
confirm_password: str
@field_validator("confirm_password")
def check_password_match(cls, v: str, info: ValidationInfo) -> str:
# 从info.data中获取已经校验过的password字段值
password = info.data.get("password")
# 如果password已经存在且两个值不一致,抛出校验错误
if password is not None and v != password:
raise ValueError("确认密码和密码不一致")
return v
# 测试正常情况
try:
user = UserRegister(password="123456", confirm_password="123456")
print("校验通过:", user.model_dump())
except ValidationError as e:
print("校验失败:", e)
# 测试异常情况
try:
user = UserRegister(password="123456", confirm_password="654321")
print("校验通过:", user.model_dump())
except ValidationError as e:
print("校验失败:", e)
多字段依赖校验:时间范围校验场景
如果需要在多个字段之间做联合校验,比如开始时间和结束时间的合理性判断,可以任选一个相关字段的校验器实现,也可以同时校验多个字段。
from datetime import datetime
from pydantic import BaseModel, field_validator, ValidationError
class Task(BaseModel):
start_time: datetime
end_time: datetime
@field_validator("end_time")
def check_time_range(cls, v: datetime, info: ValidationInfo) -> datetime:
start_time = info.data.get("start_time")
if start_time is not None and v < start_time:
raise ValueError("结束时间不能早于开始时间")
return v
# 测试正常情况
try:
task = Task(
start_time=datetime(2024, 1, 1, 10, 0),
end_time=datetime(2024, 1, 1, 12, 0)
)
print("校验通过:", task.model_dump())
except ValidationError as e:
print("校验失败:", e)
# 测试异常情况
try:
task = Task(
start_time=datetime(2024, 1, 1, 10, 0),
end_time=datetime(2024, 1, 1, 8, 0)
)
print("校验通过:", task.model_dump())
except ValidationError as e:
print("校验失败:", e)
注意事项
校验顺序的影响
field_validator的校验是按照字段在模型中定义的顺序执行的,info.data中只会包含已经执行过校验的字段数据。如果跨字段校验依赖的字段在后面定义,可能会出现获取不到数据的情况,因此建议将依赖的字段定义在前面,或者在校验时先判断依赖字段是否存在。
多个字段共用校验器
如果需要同时对多个字段做跨字段校验,可以在field_validator中传入多个字段名,不过这种方式下info.data的字段数据需要结合具体场景判断,通常更推荐在其中一个核心字段的校验器中实现跨字段逻辑。
from pydantic import BaseModel, field_validator, ValidationError
class Product(BaseModel):
price: float
discount: float
final_price: float
@field_validator("price", "discount", "final_price")
def check_price_logic(cls, v: float, info: ValidationInfo) -> float:
data = info.data
# 当三个字段都存在时做联合校验
if all(k in data for k in ("price", "discount", "final_price")):
expected = round(data["price"] * (1 - data["discount"]), 2)
if abs(data["final_price"] - expected) > 0.01:
raise ValueError("最终价格计算不符合折扣逻辑")
return v
错误信息定制
校验失败时抛出的错误会被pydantic捕获并整理成标准的校验错误响应,如果需要定制错误类型或者错误提示,可以在抛出异常时指定更多的参数,或者使用pydantic.errors中的错误类型。
pydanticfield_validator跨字段校验数据验证修改时间:2026-06-15 17:03:39