首页 / FastAPI 入门教程 / Pydantic v2 核心:类型注解与模型

FastAPI 入门教程

Pydantic v2 核心:类型注解与模型

本教程共 50 篇 · 第 19 篇 · 更新于 2026-08-12 · 约 7 分钟阅读

FastAPIFastAPI 入门教程Pydantic v2BaseModelField 约束类型注解

本节目标:学会用 Python 类型注解定义 Pydantic 数据模型,能给字段加约束、标可选,并能创建对象、读属性。

FastAPI 处理请求数据、返回响应,背后都靠一个库撑着,它叫 Pydantic。你可以把它理解成「数据守门员」:你先说清楚数据长什么样,它帮你检查、转换、报错。本章只讲 Pydantic v2 的核心用法,把地基打牢,后面的校验器、序列化才好懂。

19-1 类型提示为什么重要

Python 3.5 引入了类型提示(Type Hints,用于函数参数与返回值注解),3.6 又加入了变量注解语法。它不改变程序运行结果,只是给变量、参数、返回值贴一个「类型标签」。

def get_full_name(first_name: str, last_name: str) -> str:
    return first_name.title() + " " + last_name.title()


print(get_full_name("john", "doe"))

这里 first_name: str 就是类型提示。编辑器看到它,就能在你写代码时提示方法、标出错误。FastAPI 更是把类型提示玩到了极致:你声明什么类型,它就帮你校验、转换、生成文档。

Note

类型提示只是「提示」。下面的代码不会在运行时自动报错,真正帮你拦住脏数据的是 Pydantic。

19-2 标准类型注解

Pydantic 模型里的字段,用的就是这些基础类型:intfloatstrboolbytes。写法直接在字段后面加冒号。

from pydantic import BaseModel


class Item(BaseModel):
    name: str
    price: float
    on_sale: bool
    stock: int

这四个字段都是「必需」的,创建对象时必须都给值,否则 Pydantic 直接抛错。所谓必需,就是字段没有默认值。

19-3 定义你的第一个 BaseModel

Pydantic 里所有数据模型都继承自 BaseModel。你把字段写成类属性,每个属性带一个类型提示即可。

from pydantic import BaseModel


class User(BaseModel):
    id: int
    name: str
    age: int


u = User(id=1, name="小明", age=18)
print(u.name)   # 小明
print(u)        # id=1 name='小明' age=18

User(id=1, name="小明", age=18) 就是在实例化模型。创建好后,u.name 直接拿到属性值,编辑器也会提示这些属性。

19-4 默认值让字段变成可选

如果一个字段给了默认值,那它就不是必需的了。常见做法是用 None 当默认值,表示「可以不传」。

from pydantic import BaseModel


class Item(BaseModel):
    name: str
    price: float
    description: str | None = None
    tax: float | None = None

这里 descriptiontax 都有默认值 None,所以下面两种数据都合法:

Item(name="手机", price=2999.0)
Item(name="手机", price=2999.0, description="新款", tax=0.1)
Tip

现代 Python(3.10+)推荐用 str | None 这种写法,比老的 Optional[str] 更直观。本教程全程使用 X | None

19-5 用 Field() 给字段加约束

光有类型还不够。比如价格不能是负数,名字不能太长,这时就用 Field()。它从 pydantic 直接导入,用来给字段加校验和说明。

from pydantic import BaseModel, Field


class Item(BaseModel):
    name: str = Field(..., max_length=300, description="商品名称")
    price: float = Field(..., gt=0, description="价格必须大于 0")
    stock: int = Field(default=0, ge=0, description="库存数量")

几个常用参数要记住:

  • ... 表示「必填,没有默认值」。
  • default=0 给一个默认填充值。
  • gt(大于)、ge(大于等于)、lt(小于)、le(小于等于)做数值范围。
  • max_lengthmin_length 限制字符串长度。
  • descriptiontitle 写给人看的说明,会进文档。

如果传进来的价格是负数,gt=0 会立刻拦下并报告错误。

19-6 标准泛型:list、dict、tuple、set

字段里常要放「一组值」。Python 3.9 之后可以直接用 list[str]dict[str, int] 这种标准写法,不用再从 typing 导入大写开头的 ListDict

from pydantic import BaseModel


class Student(BaseModel):
    id: int
    name: str
    subjects: list[str] = []
    scores: dict[str, int] = {}
    tags: set[str] = set()

这表示:subjects 是个字符串列表,scores 是「科目名 → 分数」的字典,tags 是字符串集合。Pydantic 会检查里面每个元素的类型对不对。

s = Student(
    id=1,
    name="小红",
    subjects=["语文", "数学"],
    scores={"语文": 90, "数学": 95},
)
print(s.subjects)   # ['语文', '数学']

19-7 嵌套模型

模型里还能嵌套另一个模型。比如订单里包含多个商品,每个商品又是一个独立模型。

from pydantic import BaseModel, Field


class Product(BaseModel):
    name: str
    price: float = Field(..., gt=0)


class Order(BaseModel):
    order_id: int
    items: list[Product]

items 是一个 Product 列表。你传进去的字典列表,Pydantic 会自动逐个变成 Product 对象。

order = Order(
    order_id=101,
    items=[
        {"name": "键盘", "price": 199.0},
        {"name": "鼠标", "price": 99.0},
    ],
)
print(order.items[0].name)   # 键盘

19-8 自动类型转换很贴心

Pydantic 会尽量把能转的类型转过去。比如你传字符串 "18"int 字段,它能自动变成整数 18

from pydantic import BaseModel


class User(BaseModel):
    id: int
    age: int


u = User(id="1", age="18")
print(u.id, type(u.id))   # 1 <class 'int'>

但如果完全转不了,比如把列表传给 int,Pydantic 就抛出 ValidationError,清楚告诉你哪个字段、什么错。

Tip

这种「宽松转换」是 Pydantic 的默认行为。想更严格可以配置 strict=True,但初学阶段先用默认即可。

19-9 在 FastAPI 里直接用模型收请求体

模型最常用在 API 的请求体。你只要把参数类型声明成模型,FastAPI 就自动读 JSON、校验、转换。

from fastapi import FastAPI
from pydantic import BaseModel, Field

from typing import Any


class Item(BaseModel):
    name: str
    price: float = Field(..., gt=0)
    description: str | None = None


app = FastAPI()


@app.post("/items/")
async def create_item(item: Item) -> dict[str, Any]:
    return item.model_dump()

客户端发来的 JSON 会被塞进 Item,校验通过后你就能用 item.nameitem.price 直接取用。一旦价格不合法,FastAPI 自动返回 422 错误和详细原因,你不用手写任何判断。

19-10 数据不合法时长什么样

学会「正确的写法」还不够,你还得知道「错的时候报什么」,调试时才不慌。当某字段违反约束,Pydantic 抛出 ValidationError,它会逐条列出错误。

from pydantic import BaseModel, Field, ValidationError


class Item(BaseModel):
    name: str = Field(..., max_length=5)
    price: float = Field(..., gt=0)


try:
    Item(name="这是一个超长的名字", price=-1)
except ValidationError as e:
    print(e)

错误信息会告诉你:哪个字段(name)、什么类型错误(string_too_long)、输入值是什么。在 FastAPI 里,这种错误会被自动变成 422 响应,你不用自己写判断。

Tip

想看完整结构,可以把错误转成 JSON:e.errors() 返回列表,每项含 loctypemsg 等键,方便你写前端提示。

19-11 为什么不直接用字典

有人会问:我用普通 dict 存数据不行吗?行,但你会失去三样东西。第一,没有编辑器提示,写 item["nmae"] 拼错也不会被发现。第二,没有自动类型转换,"18" 永远是字符串。第三,没有统一校验,每个函数都要自己写一堆 if。Pydantic 把这三件事一并包办,还顺手生成接口文档。

19-12 小结

本章你掌握了 Pydantic v2 的地基:BaseModel 定义模型、用类型注解声明字段、用 Field() 加约束、用 X | None 标可选、用 list[str] 等标准泛型描述集合,以及创建对象、读属性、自动转换、查看校验错误。记住,所有写法都是 v2 风格——没有 Optional、没有旧式 List、没有 .dict()。下一章我们给模型加上「自定义校验逻辑」。