首页
/ FastAPI 进阶实战:用 Dataclasses 声明请求体、响应模型与嵌套数据结构

FastAPI 进阶实战:用 Dataclasses 声明请求体、响应模型与嵌套数据结构

2026-09-06 14:20:26作者:滕妙奇

本文基于 FastAPI 官方文档《Using Dataclasses》展开,讲清楚 FastAPI 对标准库 dataclasses 的原生支持机制:你可以把已有的标准 @dataclass 类直接当作请求体、response_model 和嵌套数据结构来用,而底层由 Pydantic 完成验证、序列化与 OpenAPI 文档生成。读完本篇,你将掌握标准 dataclass 与 pydantic.dataclasses 两种写法在 FastAPI 中的适用场景,并能从源码层面理解 FastAPI 是如何识别并转换这些 dataclass 的。

FastAPI 使用 dataclass 作为 response_model 时,API 文档界面自动展示对应 Schema

为什么 FastAPI 能直接使用标准库 dataclasses

FastAPI 构建在 Pydantic 之上,官方教程中一直演示用 Pydantic 模型来声明请求和响应。但 Pydantic 本身内置了对标准库 dataclasses 的支持,因此 FastAPI 也支持同样的用法:即使你的代码没有显式引入 Pydantic,FastAPI 也会借助 Pydantic 把你定义的标准 dataclass 转换成 Pydantic 自己的 dataclass 形式,并由此获得与 Pydantic 模型完全一致的能力:

  • 数据验证(data validation)
  • 数据序列化(data serialization)
  • 数据文档化(data documentation),例如自动生成 OpenAPI Schema

需要注意的是:dataclasses 不能做 Pydantic 模型能做的所有事情,某些复杂场景下你可能仍需回到 Pydantic 模型。但如果你手头已经有一批现成的标准库 dataclass,用它们直接驱动一个 FastAPI Web API 是一个非常实用的技巧。

这个特性自 FastAPI 版本 0.67.0 起可用。

用标准库 dataclass 作为请求体

最直接的用法是:定义一个标准 @dataclass,然后把它作为路径操作函数的参数注解。下面代码来自 docs_src/dataclasses_/tutorial001_py310.py,完整代码如下:

from dataclasses import dataclass

from fastapi import FastAPI


@dataclass
class Item:
    name: str
    price: float
    description: str | None = None
    tax: float | None = None


app = FastAPI()


@app.post("/items/")
async def create_item(item: Item):
    return item

要点解析:

  1. Item 是一个标准的 dataclasses.dataclass,未引入任何 Pydantic 组件。
  2. descriptiontax 声明为 str | None = None / float | None = None,即可选字段,默认值为 None;而 nameprice 没有默认值,因此是必填字段
  3. 路径操作函数 create_item 直接以 item: Item 声明请求体。FastAPI 会自动解析 JSON 请求体、验证字段、构造 Item 实例并返回。

仓库中的自动化测试 tests/test_tutorial/test_dataclasses/test_tutorial001.py 验证了这一行为:POST /items/ 提交 {"name": "Foo", "price": 3} 后返回 200,响应 JSON 包含补齐默认值后的完整结构(description: Nonetax: None),证明验证与默认值填充确实生效。

源码层面的原理:FastAPI 在判断一个字段注解是“标量”还是“复杂结构”时,会显式检查 is_dataclass。见 fastapi/_compat/shared.py

def _annotation_is_complex(annotation: type[Any] | None) -> bool:
    return (
        lenient_issubclass(annotation, (BaseModel, Mapping, UploadFile))
        or _annotation_is_sequence(annotation)
        or is_dataclass(annotation)
    )

正是这个 is_dataclass(annotation) 分支,使得标准 dataclass 注解被归类为“复杂”结构,从而被当作请求体(body)字段处理,而不是误判为单个 query/path 参数。随后 Pydantic 在验证阶段将其转换为自己的 dataclass 表示。

response_model 中使用 dataclasses

dataclass 同样可以传入 response_model 参数。下面代码来自 docs_src/dataclasses_/tutorial002_py310.py

from dataclasses import dataclass, field

from fastapi import FastAPI


@dataclass
class Item:
    name: str
    price: float
    tags: list[str] = field(default_factory=list)
    description: str | None = None
    tax: float | None = None


app = FastAPI()


@app.get("/items/next", response_model=Item)
async def read_next_item():
    return {
        "name": "Island In The Moon",
        "price": 12.99,
        "description": "A place to be playin' and havin' fun",
        "tags": ["breater"],
    }

这里有几个值得注意的细节:

  1. 使用了标准库的 field(default_factory=list) 为列表字段设置默认值——这是标准 dataclass 的惯用写法,FastAPI 完全兼容。
  2. response_model=Item 中的 Item 是一个标准 dataclass,它会被自动转换为 Pydantic dataclass
  3. 由于转换后的 dataclass 拥有完整的字段 Schema,它会出现在 API 文档用户界面(Swagger UI / ReDoc)中,作为 response_model 的响应 Schema 展示。
  4. 路径操作函数返回的是普通字典而非 dataclass 实例,FastAPI 依然能按 Item 的结构完成过滤、验证与 JSON 序列化(注意 tax 字段因未在字典中提供而取默认值 None)。

对应的测试 tests/test_tutorial/test_dataclasses/test_tutorial002.py 断言 GET /items/next 返回 200 且 JSON 结构符合 Item 的完整字段。

更广泛的序列化行为在 tests/test_serialize_response_dataclass.py 中有系统覆盖,包括:返回 dataclass 实例、返回字典、返回需要类型强制转换的值(如 ISO 格式日期字符串、字符串形式的浮点数)、response_model=list[Item] 的列表场景,以及不设置 response_model 时直接返回 dataclass 实例的场景——所有情况下 datetime 都会被序列化为 ISO 字符串,可选字段缺省输出 null

源码层面response_model 的字段收集逻辑同样识别 dataclass。见 fastapi/_compat/v2.pyget_model_fields

def get_model_fields(model: type[BaseModel]) -> list[ModelField]:
    model_fields: list[ModelField] = []
    for name, field_info in model.model_fields.items():
        type_ = field_info.annotation
        if lenient_issubclass(type_, (BaseModel, dict)) or is_dataclass(type_):
            model_config = None
        else:
            model_config = model.model_config
        ...

当外层模型中嵌套了 dataclass 类型字段时,会按独立模型处理而不继承外层 config,保证嵌套 dataclass 按自身规则生成 Schema。此外,即便完全没有走 response_model,FastAPI 的 JSON 编码器也内置了 dataclass 分支,见 fastapi/encoders.py

if dataclasses.is_dataclass(obj):
    assert not isinstance(obj, type)
    obj_dict = dataclasses.asdict(obj)
    return jsonable_encoder(
        obj_dict,
        include=include,
        exclude=exclude,
        ...
    )

jsonable_encoder 通过 dataclasses.asdict() 把 dataclass 实例递归展开为字典,再逐项编码为可 JSON 化的结构,并支持 include/exclude/exclude_none 等过滤参数。

嵌套数据结构与 pydantic.dataclasses 的替换

你还可以把 dataclasses 与其他标准类型注解组合,构造嵌套数据结构。

某些情况下(例如自动生成的 API 文档出错时),你可能需要使用 Pydantic 版本的 dataclasses。此时只需把标准 dataclasses 换成 pydantic.dataclasses——它是**可直接替换(drop-in replacement)**的,而 field 仍然从标准库 dataclasses 导入即可。

下面代码来自 docs_src/dataclasses_/tutorial003_py310.py,完整示例:

from dataclasses import field  # (1)

from fastapi import FastAPI
from pydantic.dataclasses import dataclass  # (2)


@dataclass
class Item:
    name: str
    description: str | None = None


@dataclass
class Author:
    name: str
    items: list[Item] = field(default_factory=list)  # (3)


app = FastAPI()


@app.post("/authors/{author_id}/items/", response_model=Author)  # (4)
async def create_author_items(author_id: str, items: list[Item]):  # (5)
    return {"name": author_id, "items": items}  # (6)


@app.get("/authors/", response_model=list[Author])  # (7)
def get_authors():  # (8)
    return [  # (9)
        {
            "name": "Breaters",
            "items": [
                {
                    "name": "Island In The Moon",
                    "description": "A place to be playin' and havin' fun",
                },
                {"name": "Holy Buddies"},
            ],
        },
        {
            "name": "System of an Up",
            "items": [
                {
                    "name": "Salt",
                    "description": "The kombucha mushroom people's favorite",
                },
                {"name": "Pad Thai"},
                {
                    "name": "Lonely Night",
                    "description": "The mostests lonliest nightiest of allest",
                },
            ],
        },
    ]

逐条注释:

  1. field 仍然从标准 dataclasses 导入。
  2. pydantic.dataclassesdataclasses 的直接替换(drop-in replacement),只需修改导入语句。
  3. Author dataclass 内嵌了一个 list[Item] 字段——即 dataclass 中包含 dataclass 列表,构造嵌套结构。
  4. Author dataclass 被用作 response_model 参数。
  5. 请求体使用了标准类型注解 list[Item]——dataclass 可以直接和 list[...] 等通用类型组合作为请求体。
  6. 路径操作函数返回一个包含 items(dataclass 列表)的字典。FastAPI 依然能把数据序列化为 JSON。
  7. 此处的 response_model 使用 list[Author] 类型注解——再次体现 dataclass 与标准类型注解的自由组合。
  8. 注意该路径操作函数用的是普通 def 而非 async def。在 FastAPI 中 defasync def 可按需混用;如果对何时使用哪个拿不准,可以回顾官方文档中关于 asyncawait 的“In a hurry?”章节(见 docs/en/docs/async.md)。
  9. 该函数并没有返回 dataclass(虽然也可以返回),而是返回了包含内部数据的字典列表。FastAPI 会使用 response_model(其中包含 dataclass)参数来转换响应。

你可以把 dataclasses 与其他类型注解以多种不同组合方式搭配,构造出复杂的嵌套数据结构;上面的代码内注释给出了具体的细节指引。

对应的验证测试位于 tests/test_tutorial/test_dataclasses/test_tutorial003.py,覆盖了两个端点的请求验证、路径参数注入与嵌套序列化行为。

dataclasses 与 Pydantic 模型的混用与局限

dataclasses 与 Pydantic 模型之间没有墙:你可以把 dataclasses 与其他 Pydantic 模型组合使用、从 Pydantic 模型继承、把自己的 dataclass 包含进 Pydantic 模型中,等等。深入用法可以查阅 Pydantic 官方文档中关于 dataclasses 的章节(Pydantic 支持把标准库 dataclass 直接用于 BaseModel 等场景,这也是 FastAPI 支持能力的源头)。

再强调一遍文档中的关键提醒:dataclasses 不能做 Pydantic 模型能做的所有事情。如果你的需求超出标准 dataclass 的能力范围(例如复杂的字段级验证器、动态配置模型行为等),仍然需要使用 Pydantic 模型。但如果项目中已经存在一批标准 dataclass,直接用它们驱动 FastAPI API 是一条非常省事的路径。

版本要求与适用前提

  • 该特性自 FastAPI 0.67.0 版本起可用,当前仓库源码基于 Pydantic v2 的兼容层实现,因此标准 dataclass 与 pydantic.dataclasses 两种写法均受支持。
  • 文档示例代码使用 str | Nonelist[str] 等 Python 3.10+ 语法(源码文件名中的 _py310 后缀即表明最低 Python 版本为 3.10);测试中通过 tests/utils.pyneeds_py310 标记控制运行条件。如果你的 Python 版本较低,可改用 Optional[str]List[str] 等价写法。
  • 组合 dataclass 与 Pydantic 模型时,行为以 Pydantic 对 dataclass 的支持为准;遇到自动生成的 API 文档异常时,优先尝试切换为 pydantic.dataclasses

关键文件索引

内容 仓库相对路径
本文对应的官方文档 docs/en/docs/advanced/dataclasses.md
请求体示例(标准 dataclass) docs_src/dataclasses_/tutorial001_py310.py
response_model 示例 docs_src/dataclasses_/tutorial002_py310.py
嵌套结构与 pydantic.dataclasses 示例 docs_src/dataclasses_/tutorial003_py310.py
复杂注解识别(is_dataclass 分支) fastapi/_compat/shared.py
字段收集中的 dataclass 处理 fastapi/_compat/v2.py
JSON 编码器的 dataclass 分支 fastapi/encoders.py
教程自动化测试 tests/test_tutorial/test_dataclasses/
dataclass 响应序列化测试 tests/test_serialize_response_dataclass.py
登录后查看全文
热门项目推荐
相关项目推荐