Skip to content

第 3.3 章:使用 Pydantic 的数据模型

学习时间: 50 分钟


1. Pydantic:“宇宙飞船的数字蓝图”

想象一下,你正在建造一艘宇宙飞船。你不能随意焊接金属碎片。你需要一份详细的蓝图,它定义:

  • 飞船名称 (类型: 字符串, 最大长度: 50 字符)
  • 发射年份 (类型: 整数)
  • 是否有超光速引擎 (类型: 是/否)

Pydantic 是一个库,它允许你为 Python 中的数据创建这样的“数字蓝图”。在 FastAPI 中,它执行三个关键功能:

  1. 结构声明: 清楚地描述你的数据由哪些字段组成。
  2. 数据验证: 自动检查传入数据是否符合蓝图。
  3. 文档: FastAPI 使用这些蓝图来生成详细且交互式的文档。

💡 宇宙类比: Pydantic 模型是对象的技术护照。任何抵达空间站的“货物”(数据)都必须符合护照中的规范。如果不符合——机载计算机 (Pydantic) 将拒绝它。


2. 创建第一个蓝图:Spaceship 模型

让我们创建一个模型来描述我们的宇宙飞船。

步骤 1:从 Pydantic 导入 BaseModel Pydantic 已经随 fastapi[all] 一起安装。我们只需要为我们的模型导入基类。

main.py 文件顶部,与其他导入一起添加:

# main.py
from fastapi import FastAPI
from pydantic import BaseModel

步骤 2:描述 Spaceship 模型 创建一个继承自 BaseModel 的类。在类内部,使用标准的 Python 类型提示定义字段及其类型。

将此代码添加到 main.py (可以在导入之后):

class Spaceship(BaseModel):
    """
    宇宙飞船的技术护照(模型)。
    """
    name: str
    type: str
    launch_year: int
    status: str
就是这样!你刚刚创建了一个“蓝图”。Pydantic 现在知道,任何 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 运行。

  1. 检查 http://127.0.0.1:8000/spaceships 现在响应是一个 JSON 数组,而不是一个对象。这对于集合来说是一个更正确和标准的结构。
    [
      { "name": "Voyager-1", "type": "探测器", ... },
      { "name": "Hubble Space Telescope", ... }
    ]
    
  2. 检查 http://127.0.0.1:8000/spaceships/1 响应没有改变,但现在它保证符合模型。
  3. 查看 /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 请求时生效。


巩固知识小测验

1. Pydantic 在 FastAPI 中用于...

2. 要创建一个数据模型,需要从以下哪个类继承?

3. `@app.get` 装饰器中的 `response_model` 参数用于...

4. 如何指定端点返回 `Item` 类型对象的*列表*?

5. Pydantic 模型中的 `Field(..., gt=0)` 意味着该字段...


🚀 本章总结:

你为 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())