在 FastAPI 中,Pydantic 模型是数据验证、序列化和文档生成的绝对核心。FastAPI 正是通过读取 Pydantic 模型中的 Python 类型提示,来实现自动的数据校验和 Swagger 文档生成的。
目前 FastAPI 默认且强烈推荐使用 Pydantic V2(基于 Rust 的 pydantic-core,速度极快)。下面我将从基础定义、字段控制、进阶验证到最佳实践,为你系统梳理 FastAPI 中的 Pydantic 模型。
一、 基础语法:定义你的第一个模型
只需继承 BaseModel,并使用 Python 的类型提示 (Type Hints) 即可。
from pydantic import BaseModel
class Item(BaseModel):
name: str
price: float
is_offer: bool | None = None # 可选字段,默认值为 None在 FastAPI 中使用:
from fastapi import FastAPI
app = FastAPI()
@app.post("/items/")
def create_item(item: Item):
# FastAPI 会自动将请求的 JSON 解析并验证为 Item 对象
# 如果 price 是字符串 "abc",会自动返回 422 错误
return {"item_name": item.name, "item_price": item.price}二、 强大的字段控制:Field 🌟
Field 是 Pydantic 的利器,用于为字段添加校验规则、默认值、文档描述和示例(这些都会自动同步到 Swagger UI 中)。
from pydantic import BaseModel, Field
from typing import Annotated # Python 3.9+ 推荐用法
class UserCreate(BaseModel):
# 基础校验:长度限制
username: str = Field(..., min_length=3, max_length=50, description="用户登录名")
# 格式校验:内置的 EmailStr 需要安装 pydantic[email]
email: str = Field(..., pattern=r'^[\w\.-]+@[\w\.-]+\.\w+$', description="电子邮箱")
# 数值校验:ge (>=), le (<=), gt (>), lt (<)
age: int = Field(default=18, ge=18, le=120, description="用户年龄")
# 现代写法:使用 Annotated (更清晰,推荐)
status: Annotated[str, Field(description="账户状态", examples=["active", "inactive"])] = "active"💡 提示:... (Ellipsis) 表示该字段是必填项 (Required)。
三、 Pydantic 在 FastAPI 中的“双重角色”
同一个模型,既可以用作请求体验证,也可以用作响应模型过滤。
1. 作为 Request Body (输入验证)
确保客户端传来的数据符合规范,不符合则直接拦截,保护业务逻辑。
2. 作为 Response Model (输出过滤与序列化)
这是 Pydantic 最优雅的特性。它可以自动过滤掉模型中未定义的字段(例如密码、内部 ID),并确保返回的数据类型正确。
class UserIn(BaseModel):
username: str
password: str # 敏感信息
class UserOut(BaseModel):
username: str
# 注意:这里没有 password 字段
@app.post("/users/", response_model=UserOut)
def register_user(user: UserIn):
# 模拟保存到数据库
db_user = {"username": user.username, "password": "hashed_" + user.password, "internal_id": 999}
# 即使返回了包含 password 和 internal_id 的字典
# response_model=UserOut 也会自动将其过滤,只返回 username
return db_user
# 客户端实际收到: {"username": "alice"}四、 进阶技巧 (Pydantic V2 专属)
1. 嵌套模型 (Nested Models)
轻松处理复杂的 JSON 结构。
class Address(BaseModel):
city: str
zipcode: str
class User(BaseModel):
name: str
address: Address # 嵌套另一个模型
# 接收的 JSON: {"name": "Alice", "address": {"city": "Beijing", "zipcode": "100000"}}2. 模型继承 (Model Inheritance)
复用字段定义,避免重复代码。
class BaseUser(BaseModel):
username: str
email: str
class UserCreate(BaseUser):
password: str # 继承 username, email,并新增 password
class UserUpdate(BaseUser):
password: str | None = None # 继承 username, email,password 变为可选3. 自定义验证器 (@model_validator)
当单个字段的规则不够,需要跨字段校验或复杂逻辑时使用。
from pydantic import BaseModel, model_validator
class Event(BaseModel):
start_time: int
end_time: int
@model_validator(mode='after')
def check_times(self) -> 'Event':
if self.end_time <= self.start_time:
raise ValueError("结束时间必须晚于开始时间")
return self4. 计算字段 (@computed_field) (Pydantic V2 新增)
有些字段不需要客户端传入,而是根据其他字段计算得出,且需要包含在响应中。
from pydantic import BaseModel, computed_field
class Rectangle(BaseModel):
width: float
height: float
@computed_field
@property
def area(self) -> float:
return self.width * self.height
rect = Rectangle(width=10, height=5)
print(rect.model_dump())
# 输出: {'width': 10.0, 'height': 5.0, 'area': 50.0} (area 会自动包含在序列化结果中)五、 ⚠️ 常见陷阱与最佳实践
1. Pydantic V1 vs V2 语法差异 (极易踩坑)
如果你看的是旧教程,请注意以下 V2 的变化:
- ❌ 旧:
item.dict()👉 ✅ 新:item.model_dump() - ❌ 旧:
item.json()👉 ✅ 新:item.model_dump_json() - ❌ 旧:
@validator/@root_validator👉 ✅ 新:@field_validator/@model_validator - ❌ 旧:
Field(default_factory=list)👉 ✅ 新:依然支持,但也可以直接用list类型提示配合默认值。
2. 可变默认值陷阱
虽然 Pydantic V2 对可变默认值处理得比原生 Python 好,但最佳实践仍然是使用 default_factory 来生成新的列表或字典,避免多个实例共享同一个可变对象。
from pydantic import BaseModel, Field
class Team(BaseModel):
# ✅ 推荐:每次实例化都会创建一个新的空列表
members: list[str] = Field(default_factory=list)3. 区分 Optional 和 赋予默认值
age: int | None:表示该字段可以是None,但客户端必须显式传递"age": null。age: int | None = None:表示该字段是可选的,客户端可以不传,FastAPI 会自动将其设为None。
4. 保持 Request 和 Response 模型分离
尽量不要用一个模型既做输入又做输出。输入模型(如 UserCreate)可能包含密码,而输出模型(如 UserOut)应包含数据库生成的 id 和 created_at。分离它们能让代码意图更清晰。
总结:Pydantic 模型速查表
| 需求 | Pydantic 解决方案 |
|---|---|
| 定义数据结构 | 继承 BaseModel + 类型提示 |
| 必填项 | field_name: str = Field(...) |
| 可选且带默认值 | field_name: str | None = None |
| 数值/字符串范围限制 | Field(ge=0, le=100, min_length=3) |
| 跨字段复杂校验 | @model_validator(mode='after') |
| 动态计算并返回的字段 | @computed_field + @property |
| 将模型转为字典 (用于存数据库) | model.model_dump() |
掌握了 Pydantic,你就掌握了 FastAPI 数据流的“守门员”。你可以尝试用 Pydantic 定义一个你当前项目中的复杂业务对象(比如包含嵌套列表和自定义校验的订单模型),如果遇到问题,随时发给我帮你 Review!