Appearance
30|Pydantic 高级校验与嵌套模型
基础类型注解能约束数据的形状,但生产环境需要更精细的校验:邮箱格式、字符串长度、数字范围、枚举值、自定义业务规则。Pydantic 的 Field 和校验器(validator)提供了这些能力。
一、Field 约束
python
from pydantic import BaseModel, Field
class ServerCreate(BaseModel):
hostname: str = Field(..., min_length=1, max_length=64)
port: int = Field(..., ge=1, le=65535)
description: str = Field(default="", max_length=500)
tags: list[str] = Field(default_factory=list, max_length=10)| 参数 | 作用 | 适用类型 |
|---|---|---|
min_length / max_length | 字符串长度限制 | str |
ge / le / gt / lt | 数值范围 | int、float |
regex | 正则匹配 | str |
default | 默认值 | 所有类型 |
default_factory | 动态默认值(如空列表) | list、dict、set |
字符串正则校验
python
class UserCreate(BaseModel):
username: str = Field(..., min_length=3, max_length=20, regex=r"^[a-zA-Z0-9_]+$")
email: str = Field(..., regex=r"^[\w\.-]+@[\w\.-]+\.\w+$")regex 用正则表达式约束字符串格式。上面的 username 只允许字母、数字和下划线。
枚举约束
python
from enum import Enum
class Status(str, Enum):
draft = "draft"
published = "published"
archived = "archived"
class ArticleCreate(BaseModel):
title: str
status: Status = Status.draftstatus 只能是 draft、published、archived 三者之一,传入其他值返回 422。
二、自定义校验器
Field 不够用时,用 @validator 写自定义逻辑:
python
from pydantic import BaseModel, validator
class ServerCreate(BaseModel):
hostname: str
ip: str
@validator("hostname")
def hostname_must_not_be_empty(cls, v):
if not v or not v.strip():
raise ValueError("主机名不能为空")
return v.strip()
@validator("ip")
def ip_must_be_valid(cls, v):
parts = v.split(".")
if len(parts) != 4:
raise ValueError("IP 格式错误")
for part in parts:
if not part.isdigit() or not 0 <= int(part) <= 255:
raise ValueError("IP 格式错误")
return v校验器接收类(cls)和字段值(v),返回校验后的值(可以修改)。校验失败时抛出 ValueError,FastAPI 自动转为 422 响应。
跨字段校验
python
class DateRange(BaseModel):
start: str
end: str
@validator("end")
def end_must_after_start(cls, v, values):
if "start" in values and v <= values["start"]:
raise ValueError("结束时间必须晚于开始时间")
return vvalues 参数包含已经校验过的其他字段。注意:字段校验顺序按定义顺序执行,跨字段校验时要确保依赖的字段已经处理。
根校验器(校验整个模型)
python
from pydantic import root_validator
class Config(BaseModel):
enabled: bool
timeout: int = 30
@root_validator
def check_timeout_if_enabled(cls, values):
if values.get("enabled") and values.get("timeout", 0) <= 0:
raise ValueError("启用时 timeout 必须大于 0")
return values三、嵌套模型高级用法
列表嵌套
python
class Tag(BaseModel):
name: str
color: str = "#000000"
class ArticleCreate(BaseModel):
title: str
tags: list[Tag] = []请求体:
json
{
"title": "入门",
"tags": [
{"name": "FastAPI", "color": "#1890ff"},
{"name": "Python"}
]
}可选嵌套模型
python
from typing import Optional
class ArticleCreate(BaseModel):
title: str
author: Optional[Author] = Noneauthor 可以传完整对象、传 null,或不传(默认 None)。
四、Config 配置
python
class ArticleCreate(BaseModel):
title: str
content: str
class Config:
min_anystr_length = 1 # 所有字符串字段默认最小长度 1
anystr_strip_whitespace = True # 自动去除字符串首尾空格
validate_assignment = True # 赋值时也触发校验五、常见错误
校验器忘记返回值
python
@validator("hostname")
def check_hostname(cls, v):
if not v:
raise ValueError("不能为空")
# 错误:没有 return,字段值变成 None
# 正确
@validator("hostname")
def check_hostname(cls, v):
if not v:
raise ValueError("不能为空")
return v用可变对象做默认值
python
# 错误:所有实例共享同一个列表
class Item(BaseModel):
tags: list[str] = []
# 正确:用 default_factory
class Item(BaseModel):
tags: list[str] = Field(default_factory=list)校验器顺序依赖问题
python
class Model(BaseModel):
b: int
a: int
@validator("a")
def check_a(cls, v, values):
# 错误:a 在 b 前面定义,校验 a 时 b 还没处理
if v < values["b"]: # KeyError
...确保校验器依赖的字段在它之前定义,或者使用 @root_validator。