第 3.3 章:使用 Pydantic 的数据模型
学习时间: 50 分钟
1. Pydantic:“宇宙飞船的数字蓝图”
想象一下,你正在建造一艘宇宙飞船。你不能随意焊接金属碎片。你需要一份详细的蓝图,它定义:
- 飞船名称 (类型:
字符串
, 最大长度: 50 字符) - 发射年份 (类型:
整数
) - 是否有超光速引擎 (类型:
是/否
)
Pydantic 是一个库,它允许你为 Python 中的数据创建这样的“数字蓝图”。在 FastAPI 中,它执行三个关键功能:
- 结构声明: 清楚地描述你的数据由哪些字段组成。
- 数据验证: 自动检查传入数据是否符合蓝图。
- 文档: FastAPI 使用这些蓝图来生成详细且交互式的文档。
💡 宇宙类比: Pydantic 模型是对象的技术护照。任何抵达空间站的“货物”(数据)都必须符合护照中的规范。如果不符合——机载计算机 (Pydantic) 将拒绝它。
2. 创建第一个蓝图:Spaceship
模型
让我们创建一个模型来描述我们的宇宙飞船。
步骤 1:从 Pydantic 导入 BaseModel
Pydantic 已经随 fastapi[all]
一起安装。我们只需要为我们的模型导入基类。
在 main.py
文件顶部,与其他导入一起添加:
步骤 2:描述 Spaceship
模型
创建一个继承自 BaseModel
的类。在类内部,使用标准的 Python 类型提示定义字段及其类型。
将此代码添加到 main.py
(可以在导入之后):
Spaceship
类型的对象都必须有四个指定类型的字段。
3. 应用模型:改进我们的端点
现在,让我们使用新模型来使 API 更“智能”。
A. 作为响应的模型 (Response Model)
我们可以告诉 FastAPI,我们的端点应该返回符合 Spaceship
模型的数据。这确保了响应总是具有正确的结构。
按以下方式修改 /spaceships/{ship_id}
端点:
# main.py
# ... db_spaceships 和 Spaceship 模型的代码 ...
# 使用 `response_model` 指定响应的“蓝图”
@app.get("/spaceships/{ship_id}", response_model=Spaceship)
def get_spaceship(ship_id: int):
"""
返回与 Spaceship 模型匹配的飞船数据。
"""
ship = db_spaceships.get(ship_id)
return ship
response_model=Spaceship
: 我们告诉 FastAPI:“此函数的响应应符合 Spaceship
结构。过滤掉所有多余的字段,并确保类型正确。”
这带来了什么?
- 数据过滤: 如果
db_spaceships
中有多余的字段(例如,"secret_code"
),它们将不会出现在最终的 JSON 中。 - 结构保证: API 客户端可以确信始终会收到预期格式的响应。
- 文档: 在
/docs
中,现在将显示精确的响应示例 (Example Value)。
B. 集合的模型
那么 /spaceships
端点呢,它返回的是飞船的列表?为此,我们需要使用 typing
模块中的 list
。
修改导入和 /spaceships
端点:
# main.py 在顶部
from fastapi import FastAPI
from pydantic import BaseModel
from typing import List # <-- 导入 List
# ... 代码 ...
# 指定响应是 Spaceship 类型对象的列表 (List)
@app.get("/spaceships", response_model=List[Spaceship])
def get_spaceships():
"""
返回飞船列表。列表中的每个元素
都会根据 Spaceship 模型进行验证。
"""
# Pydantic 无法处理以 ID 为键的字典。
# 我们将字典转换为一个简单的列表。
return list(db_spaceships.values())
response_model=List[Spaceship]
: 我们指定响应将是一个列表,其中每个元素都是一个符合Spaceship
模型的对象。return list(db_spaceships.values())
: 重要的更改!Pydantic 期望一个可迭代对象(列表),而不是一个以 ID 为键的字典。我们将“数据库模拟器”的值转换为一个列表。
4. 验证改进后的 API
确保 uvicorn
服务器已使用 --reload
运行。
- 检查
http://127.0.0.1:8000/spaceships
: 现在响应是一个 JSON 数组,而不是一个对象。这对于集合来说是一个更正确和标准的结构。 - 检查
http://127.0.0.1:8000/spaceships/1
: 响应没有改变,但现在它保证符合模型。 - 查看
/docs
: 页面底部的“Schemas”部分出现了你的Spaceship
模型。并且端点的响应示例现在显示了美观、结构化的数据模式。
5. 高级验证:“机载计算机”在行动
Pydantic 不仅仅能检查类型,它还能做更多事情。
在我们的 Spaceship
模型中添加验证:
from pydantic import BaseModel, Field
class Spaceship(BaseModel):
name: str = Field(..., min_length=3, max_length=50, description="飞船名称")
type: str
launch_year: int = Field(..., gt=1950, description="发射年份必须在 1950 年之后")
status: str
Field(...)
: 用于添加额外的验证规则。...
(省略号): 表示该字段是必填的。min_length
,max_length
: 字符串的限制。gt
: “大于”。
尽管我们还没有创建新的飞船(这将在下一章进行),但这些规则将已经反映在文档中,并在我们实现 POST
请求时生效。
巩固知识小测验
🚀 本章总结:
你为 API 的数据设计了“数字蓝图”。现在它不仅可以工作,而且工作起来可预测且可靠。
- 📝 使用 Pydantic 创建了
Spaceship
模型。 - 🛡️ API 现在使用
response_model
验证和过滤出站数据。 - 📊 文档变得更加信息丰富,显示了精确的数据模式。
蓝图已准备好并获批准! 在下一章中,我们将从读取数据转向创建数据——为我们的舰队实现完整的 CRUD 操作。
📌 检查:
- 确保
/docs
中出现了Spaceship
模型的架构。- 检查
/spaceships
端点现在返回 JSON 数组 ([...]
),而不是对象 ({...}
)。- 确保在添加模型后代码中没有语法错误。
⚠️ 如果有错误: -
NameError: name 'BaseModel' is not defined
: 请检查您是否从pydantic
导入了BaseModel
。 -NameError: name 'List' is not defined
: 请检查您是否从typing
导入了List
。 - 对/spaceships
的响应为空 ([]
): 请确保您已将return db_spaceships
更改为return list(db_spaceships.values())
。