装饰器 · Pydantic 校验

本文覆盖 Pydantic v2 常用装饰器。FastAPI 请求体校验见 函数-路由-FastAPI;轻量函数校验见 函数-校验-validate_call

0. 一句话定位

维度 内容
作用对象 类方法(BaseModel 内)
使用场景 校验
来源 第三方 pydantic v2
语法形式 @field_validator / @model_validator / @field_serializer

段末注释:Pydantic 是基于类型注解的数据验证与序列化库,FastAPI 默认用它解析 JSON 请求体。


1. @field_validator

单个或几个字段做校验或转换。

重点参数

参数 类型 默认值 作用 配置建议
字段名 str / * 必填 要校验的字段;* 表示所有字段 多字段可传多个名
mode str "after" "before" 原始输入前;"after" 类型转换后 字符串预处理用 before
check_fields bool None 是否检查字段存在 继承模型时注意

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from pydantic import BaseModel, field_validator

class User(BaseModel):
name: str
age: int

@field_validator("name")
@classmethod
def name_not_empty(cls, v: str) -> str:
v = v.strip()
if not v:
raise ValueError("name 不能为空")
return v

@field_validator("age")
@classmethod
def age_non_negative(cls, v: int) -> int:
if v < 0:
raise ValueError("age 不能为负")
return v

print(User(name=" alice ", age=20))
# User(name='alice', age=20)

mode='before' 清洗原始输入

1
2
3
4
5
6
7
8
9
10
11
12
class Config(BaseModel):
tags: list[str]

@field_validator("tags", mode="before")
@classmethod
def split_tags(cls, v):
if isinstance(v, str):
return [t.strip() for t in v.split(",") if t.strip()]
return v

print(Config(tags="a, b , c"))
# tags=['a', 'b', 'c']

2. @model_validator

整个模型做跨字段校验或构造后处理。

重点参数

参数 类型 默认值 作用 配置建议
mode str 必填 "before"/"after"/"wrap" 跨字段用 after;包装构造用 wrap

示例:mode='after'

1
2
3
4
5
6
7
8
9
10
11
from pydantic import BaseModel, model_validator

class DateRange(BaseModel):
start: str
end: str

@model_validator(mode="after")
def end_after_start(self):
if self.end < self.start:
raise ValueError("end 必须 >= start")
return self

示例:mode='before'(原始 dict)

1
2
3
4
5
6
7
8
9
10
class Payload(BaseModel):
a: int
b: int

@model_validator(mode="before")
@classmethod
def inject_default(cls, data):
if isinstance(data, dict) and "b" not in data:
data = {**data, "b": 0}
return data

3. @field_serializer

控制字段序列化输出model_dump() / JSON 响应)。

参数 类型 作用 配置建议
字段名 str / * 要定制的字段 敏感字段可脱敏
when_used str 'always'/'unless-none'/'json'/'python' API 响应用默认即可
1
2
3
4
5
6
7
8
9
10
11
12
13
from pydantic import BaseModel, field_serializer

class Account(BaseModel):
user: str
password: str

@field_serializer("password")
def hide_password(self, v: str) -> str:
return "***"

acc = Account(user="u", password="secret")
print(acc.model_dump())
# {'user': 'u', 'password': '***'}

4. 其他常用装饰器(简表)

装饰器 用途
@computed_field 只读计算属性,参与 schema
@model_serializer 整个模型序列化行为
@validate_call 见专篇,可装饰普通函数

5. 适用 / 不适用

适用

  • API 请求/响应模型、配置类、DTO
  • 字段级 + 跨字段规则集中声明

不适用

  • 无 schema 的随意 dict → validate_call 或手动校验
  • 极复杂业务规则链 → 领域层 service + 简单 Pydantic 边界

6. 易踩坑

  • v2 的 @validator 已废弃,用 @field_validator + @classmethod
  • model_validator(mode='after') 实例方法须 return self
  • field_validatorreturn 转换后的值,不要只校验不返回
  • FastAPI response_model 会再次校验输出,validator 副作用可能导致意外
  • model_config = ConfigDict(str_strip_whitespace=True) 与手写 strip 重复时注意顺序

7. 参考

-------------本文结束感谢您的阅读-------------