FastAPI 系列
Pydantic 模型怎么当契约(当前篇)
JSON 进接口之后,不能还是一堆 dict。Pydantic 模型同时负责校验、文档和响应过滤。入参模型和出参模型必须分开,否则密码、内部标记会顺着 return user 漏出去。

入参模型和出参模型
from typing import Any
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr, Field
app = FastAPI()
class UserIn(BaseModel):
username: str = Field(min_length=3, max_length=32)
password: str = Field(min_length=8)
email: EmailStr
full_name: str | None = None
class UserOut(BaseModel):
username: str
email: EmailStr
full_name: str | None = None
@app.post("/users/", response_model=UserOut)
async def create_user(user: UserIn) -> Any:
# 这里应写入哈希后的密码,而不是原样保存
return user
response_model=UserOut 会在返回前丢掉 password。只写返回类型注解有时不够,显式 response_model 更稳,尤其是返回 ORM 对象或 dict 的时候。
校验失败长什么样
字段不合格时,FastAPI 返回 422,body 是 Pydantic 的错误列表,而不是自己拼的字符串。客户端应按字段名提示用户,不要只弹「失败」。
常用约束:
Field(ge=0)数值范围Field(min_length=1)字符串长度EmailStr需要email-validatormodel_config = ConfigDict(extra="forbid")拒绝未知字段
嵌套与列表
class Offer(BaseModel):
item_id: int
price: float
class OrderIn(BaseModel):
user_id: int
offers: list[Offer]
嵌套模型会原样出现在 OpenAPI 里。前端可以按 schema 生成表单。不要把「任意 JSON」收成 dict[str, Any],除非你真的在做网关透传。
示例写进文档
class Item(BaseModel):
name: str
price: float
model_config = {
"json_schema_extra": {
"examples": [
{"name": "Foo", "price": 42.0}
]
}
}
Swagger UI 会用这些例子预填。例子要真实,不要写 string / 0 这种占位,否则对接的人会按占位实现。
常见误区
同一个模型既接收密码又返回用户
这是最常见的数据泄漏。拆 UserIn / UserOut / UserInDB。
在路径函数里手动 if 校验每个字段
范围、格式、必填应放进模型。路径函数只处理业务规则,例如「库存够不够」。
把 ORM 对象直接 JSON 序列化
关系字段、延迟加载、内部列都会爆。用响应模型,或 Pydantic 的 from_attributes=True 显式转换。
小结
模型是接口契约:进什么、出什么、校验什么。下一篇按模块拆 Router,避免所有路径都堆在 main.py。
参考资料
Pydantic 模型怎么当契约:校验、响应模型和示例
https://lautung.com/archives/fastapi-04-pydantic
评论