第 06 章 数据校验 Schema
学习目标
学完本章,你应该能:
- 理解 Schema 的作用,以及它和 Model 的区别
- 掌握 fba 中 Schema 的命名规范
- 能定义请求入参和响应出参模型
一、Schema 是什么
Schema 是数据的"模板",用来定义和校验数据的形状。它基于 Pydantic 库实现。
Pydantic:Python 最流行的数据校验库,能自动验证数据类型、做格式转换、生成文档。fba 用的是 Pydantic v2。
Schema vs Model 的区别
这是新手最容易混淆的点:
| 对比项 | Model(第 05 章) | Schema(本章) |
|---|---|---|
| 对应 | 数据库表结构 | 接口的输入/输出数据 |
| 用途 | 操作数据库 | 校验请求、格式化响应 |
| 库 | SQLAlchemy | Pydantic |
| 变化 | 跟表结构走 | 跟接口需求走 |
一句话:Model 管"数据存进数据库的样子",Schema 管"数据进出接口的样子"。同一个 Model 可以对应多个 Schema(新增要这些字段,查询要那些字段)。
Model 与 Schema 的配合关系
二、fba 的 Schema 基类
fba 对 Schema 做了大量定制,核心在 backend/common/schema.py 的 SchemaBase 类:
class SchemaBase(BaseModel):
model_config = ConfigDict(
populate_by_name=True, # 允许用原始字段名或别名赋值
alias_generator=to_camel, # 自动把字段名转成小驼峰
use_enum_values=True,
)关键点:
- 驼峰返回:Python 习惯用下划线命名(
user_name),前端习惯用小驼峰(userName)。SchemaBase自动做转换,返回给前端的数据会自动变成小驼峰。 - 所有自定义 Schema 都继承
SchemaBase。
小驼峰命名:第一个单词小写,后面单词首字母大写,如
userName、createdTime。
驼峰转换示意
三、类命名规范
fba 对 Schema 类命名有严格规范,强烈建议遵守(AI 生成代码也遵循此规范):
| 类名 | 用途 |
|---|---|
XxxSchemaBase | 基础 Schema(公共字段) |
XxxParam | 接口入参 |
CreateXxxParam | 新增入参 |
UpdateXxxParam | 更新入参 |
DeleteXxxParam | 批量删除入参 |
GetXxxDetail | 查询详情出参 |
GetXxxWithJoinDetail | 查询详情(带 join)出参 |
GetXxxWithRelationDetail | 查询详情(带关系)出参 |
GetXxxTree | 查询树形结构出参 |
记忆技巧:Create/Update/Delete/Get 前缀 + 业务名 + Param/Detail 后缀。
Schema 家族关系图
四、Field 定义规范
fba 建议:
- 不建议用
...表示必填(Pydantic 的省略号写法),参考官方文档用更明确的方式 - 建议为所有字段加
description,这对 API 文档非常有用
示例:
from pydantic import Field
class CreateUserParam(SchemaBase):
username: str = Field(description='用户名')
password: str = Field(description='密码')
nickname: str | None = Field(default=None, description='昵称')Field:Pydantic 提供的字段描述函数,可设置默认值、描述、校验规则等。
五、完整示例:用户模块的 Schema
结合命名规范,一个用户模块的 Schema 可能长这样:
class UserSchemaBase(SchemaBase):
"""用户基础字段"""
username: str = Field(description='用户名')
nickname: str | None = Field(default=None, description='昵称')
class CreateUserParam(UserSchemaBase):
"""新增用户入参"""
password: str = Field(description='密码')
class UpdateUserParam(SchemaBase):
"""更新用户入参(字段都可选)"""
nickname: str | None = Field(default=None, description='昵称')
class GetUserDetail(UserSchemaBase):
"""查询用户详情出参"""
id: int = Field(description='用户ID')
created_time: datetime = Field(description='创建时间')注意:
- 新增和更新的入参字段不同(新增要密码,更新可能只要昵称)
- 出参(Detail)通常比入参多出 id、时间戳等系统字段
六、Schema 在接口中的使用
Schema 最终用在 API 层的路由里,FastAPI 会自动用它校验数据:
@router.post('/user', summary='创建用户')
async def create_user(obj: CreateUserParam) -> ResponseModel:
...当请求进来时,FastAPI 会:
- 自动校验请求体是否符合
CreateUserParam - 类型不匹配或缺少必填字段时,自动返回 422 错误
- 校验通过后,把数据以
CreateUserParam对象传入函数
数据校验流程
七、常见疑问
Q1:为什么更新入参字段都要设为可选? 因为更新接口通常只更新部分字段(比如只改昵称不改密码),设为可选才能"传哪个改哪个"。
Q2:同一个 Model 为什么要多个 Schema? 因为"新增"和"查询"需要的字段不同:新增需要密码,查询返回的是 id + 时间戳,不该把密码暴露出去。分多个 Schema 能精确控制每个接口的字段。
本章小结
关键要点回顾:
- Schema 基于 Pydantic v2,用于校验接口输入/输出数据
- Model 管数据库,Schema 管接口,两者不要混淆
- 命名规范:Create/Update/Delete/Get 前缀 + Param/Detail 后缀
- 继承
SchemaBase自动获得驼峰转换 - 字段记得写
description,方便生成 API 文档
下一步: 数据定义好了(Model + Schema),第 07 章学习如何定义路由(Router),把接口暴露出去。
对应官方文档:
schema(backend/reference/schema.html)