pydantic v2中basemodel默认不校验字段缺失,需显式用field(...)声明必填;字符串清洗须用@field_validator(mode='before');字段别名用field(alias=...)统一映射;批量验证错误应调用errors(include_url=false)精准定位。

Pydantic v2 里 BaseModel 默认不校验字段缺失,得手动设 strict=True 或用 Field(...)
清洗后的数据常有字段缺失或类型混杂,比如 CSV 解析后数字列混入空字符串。Pydantic v2 默认对未传字段不做缺失检查——哪怕你写了 int 类型注解,{} 也能过验证。这不是 bug,是设计选择,但容易让人误以为“类型已兜底”。
真正强制非空且类型精准,得显式声明:
from pydantic import BaseModel, Field <p>class CleanedRecord(BaseModel): user_id: int = Field(...) # ... 表示必填 email: str = Field(..., min_length=5) score: float</p>
注意:score: float 这种写法在字段没传时不会报错(默认允许 None),只有传了值才校验类型。要统一强制非空,每个字段都得加 = Field(...)。
清洗后含空格/换行的字符串,str.strip() 要在 @field_validator 里做,别靠 default_factory
常见清洗动作如去首尾空格、合并连续空白,不能只靠预处理函数塞进 default_factory,因为 Pydantic 的 default_factory 只在字段缺失时触发,而清洗后字段存在但值为 " abc\n " 时它完全不生效。
正确做法是用 @field_validator 配合 mode='before':
from pydantic import field_validator
<p>class CleanedRecord(BaseModel):
name: str</p><pre class="brush:php;toolbar:false;"><pre class="brush:php;toolbar:false;">@field_validator('name', mode='before')
def strip_name(cls, v):
if isinstance(v, str):
return v.strip()
return v
- 必须用
mode='before',否则校验器收到的是已转成 <code>str的值,再 strip 没意义 - 要判
isinstance(v, str),否则None或数字传进来会炸 - 别在
__init__里手动 strip——绕过 Pydantic 校验链,后续字段依赖会出问题
嵌套结构清洗后字段名不一致,用 alias 映射比改原始数据更稳
比如清洗后把 "user_name" 统一转成 "username",但上游系统仍可能吐 "userName" 或 "USER_NAME"。硬编码转换逻辑易漏、难维护。
Python 3.14.2是Python编程语言在2025年12月5日发布的稳定版本,属于3.14系列的第二个维护更新。该版本包含了18项修复,重点解决了多进程、数据类及正则表达式等模块的回归问题,并修复了CVE-2025-12084等安全漏洞。此版本标志着自由线程模式(移除GIL)正式获得官方支持,是Python发展的重要里程碑。
Pydantic 支持字段别名,在模型定义层收口:
class CleanedRecord(BaseModel):
username: str = Field(alias='user_name')
full_name: str = Field(alias='fullName')
# 甚至支持多别名(v2.5+):
# email: str = Field(validation_alias=AliasChoices('email', 'user_email'))
这样无论输入是 {"user_name": "a"} 还是 {"fullName": "b"},都能被正确绑定到对应字段。注意:alias 只影响反序列化(model_validate),序列化时默认仍用字段名,需设 by_alias=False 才输出别名键。
批量验证失败时 ValidationError 的 error_details 不够直白,得用 errors(include_url=False) 提取关键信息
清洗后一批数据进 model_validate,报错堆栈里 ValidationError 默认带冗长路径和 URL,对定位具体哪条记录、哪个字段出问题帮助不大。
实用提取方式:
try:
records = [CleanedRecord.model_validate(item) for item in raw_data]
except ValidationError as e:
for error in e.errors(include_url=False):
print(f"字段 {error['loc']}: {error['msg']} (输入值: {error.get('input')})")
重点看 error['loc'](元组形式,如 ('user_id',) 或 (0, 'email')),它能准确定位到第几条数据的哪个字段;error['msg'] 是人话提示,比如 "Input should be a valid integer";error.get('input') 是原始脏值,方便回溯清洗环节漏了什么。
别依赖 str(e)——它拼接所有错误,字段位置和原始值全丢了,调试成本翻倍。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










