Pydantic v2 中 BaseModel 的验证仅在实例化时触发,赋值不自动验证;需设 validate_assignment=True 才支持赋值校验;自定义校验用 @field_validator;model_dump() 默认转原生类型,可用 round_trip=True 或 mode 参数控制;嵌套错误需捕获 ValidationError 并解析 errors()。

pydantic v2 中 BaseModel 的验证何时触发?
验证只在实例化时(即调用 MyModel(...))自动执行,赋值操作本身不触发重验证。比如定义了 age: int > 0,但后续写 obj.age = -5 不会报错——这常被误认为“验证失效”,其实是设计如此。
- 验证发生在
__init__阶段,包括类型转换(如字符串转 int)和约束检查(如@field_validator) - 字段赋值走的是普通 Python 属性设置逻辑,除非你显式启用
validate_assignment=True - 若需赋值时也验证,必须在模型定义中加配置:
class User(BaseModel): age: int model_config = {"validate_assignment": True}
怎么写一个带自定义校验逻辑的字段?
用 @field_validator 装饰器,而不是旧版的 @validator(v2 已移除)。它接收原始输入值,返回处理后的值或抛出 ValueError/TypeError。
- 装饰器参数填字段名字符串,支持多个字段一次装饰
- 函数第一个参数是待校验的值,第二个可选参数是
ValidationInfo(含上下文如字段名、数据字典) - 别在 validator 里改
info.data—— 它是只读快照,修改无效 - 示例:邮箱域名白名单校验
from pydantic import BaseModel, field_validator class Contact(BaseModel): email: str @field_validator("email") def domain_must_be_company(cls, v): if not v.endswith("@example.com"): raise ValueError("only @example.com allowed") return v
为什么 model_dump() 后字段类型变了?
model_dump() 默认返回 Python 原生类型(datetime → str,Decimal → float),不是深拷贝原始对象。如果你需要保持类型不变,得关掉序列化转换:
- 加参数
round_trip=True可保留类型(但要求目标类型可被 JSON 反序列化,否则报错) - 更稳妥的做法是用
model_dump(mode="json")得到 JSON 兼容结构,或model_dump(mode="python")得到原生类型映射 - 注意:即使
mode="python",datetime这类仍可能被转成str,除非你手动配serializer
嵌套模型验证失败时错误信息太模糊?
默认错误提示只说“1 validation error”,不指明哪个嵌套字段出问题。关键在于打开 strict=True 或用 model_validate() 替代构造函数,并捕获 ValidationError 手动解析。
- 直接 print
exc.errors()能看到带loc(位置路径)的详细列表,例如[{"loc": ["address", "zip_code"], "msg": "field required"}] - 避免用
try/except ValueError捕获——必须捕获pydantic.ValidationError - 调试时建议加
raise_errors=True参数给model_validate(),它比构造函数报错更透明
复杂嵌套 + 自定义 validator 的组合容易漏掉中间层的 None 或空字典,实际校验前最好先确认输入结构是否符合预期。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











