首页 / FastAPI 入门教程 / 嵌套模型 Nested Models

FastAPI 入门教程

嵌套模型 Nested Models

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

FastAPIFastAPI 入门教程嵌套模型列表字段list嵌套校验

本节目标:学会在 Pydantic 模型里嵌套另一个模型,用 list 表达多个子项,理解嵌套 JSON 如何被自动校验,并知道返回嵌套数据时发生了什么。

现实里的数据很少是扁平的。一个商品可能带一组标签,一张图片有网址和名称,一个订单又包含多件商品。

FastAPI 借助 Pydantic,可以描述任意深度的嵌套结构。你只要把类型声明清楚,校验、转换、文档全都自动搞定。

12-1 用 list 表达多值字段

先从一个简单的需求说起:商品要带多个标签。单一字段只能存一个值,多值就要用列表。

Python 3.9 起,推荐直接写 list[str],表示「字符串组成的列表」。

from fastapi import FastAPI
from pydantic import BaseModel

class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: list[str] = []

app = FastAPI()

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    return {"item_id": item_id, "item": item.model_dump()}

这里的 tags: list[str] = [] 表示:标签是一个字符串列表,默认是空列表。客户端可以传 ["红色", "大码", "新品"] 这样的数组。

Note

方括号里的 str 叫「类型参数」,说明列表里装的是什么。这是标准 Python 语法,list[int]list[float] 同理。旧教程常写 List[str](首字母大写),本教程统一用更简洁的 list[str]

12-2 用 set 自动去重

标签通常不应该重复。Python 里有个专门存「不重复元素」的类型叫 set。把它声明成 set[str],重复数据会被自动合并。

from fastapi import FastAPI
from pydantic import BaseModel

class Item(BaseModel):
    name: str
    price: float
    tags: set[str] = set()

app = FastAPI()

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    return {"item_id": item_id, "item": item.model_dump()}

即使客户端传了 ["a", "a", "b"],Pydantic 也会把它变成 {"a", "b"}。返回时同样会去重,避免脏数据。

12-3 模型里嵌套另一个模型

更常见的场景是:一个字段本身是个复杂对象。比如商品要附一张图片,图片有网址和名称两个属性。

做法是先定义一个 Image 子模型,再把它作为 Item 的字段类型。

from fastapi import FastAPI
from pydantic import BaseModel

class Image(BaseModel):
    url: str
    name: str

class Item(BaseModel):
    name: str
    price: float
    tags: set[str] = set()
    image: Image | None = None

app = FastAPI()

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    return {"item_id": item_id, "item": item.model_dump()}

这时 FastAPI 期望的请求体长这样:

{
    "name": "Foo",
    "price": 42.0,
    "tags": ["rock", "metal"],
    "image": {
        "url": "http://example.com/baz.jpg",
        "name": "The Foo live"
    }
}

image 字段是一个嵌套的 JSON 对象,里面有 urlname。只要这样声明,FastAPI 就会自动校验内层结构,连 image.url 是不是字符串都帮你查。

12-4 嵌套结构自动校验

嵌套模型最省心的地方,是校验会一层层自动进行。你不用写任何额外代码。

  • 外层 Itemname 是必填字符串,price 是必填数字。
  • 内层 Imageurlname 也按自己的类型校验。
  • 哪一层出错,报错里的 loc 就会精确指出路径,比如 ["body", "image", "url"]

也就是说,「嵌套」只是类型声明,校验、转换、文档全部沿用同一套机制,深度不限。

12-5 列表里装子模型

标签能是字符串列表,图片当然也能是对象列表。写法一样:list[Image]

from fastapi import FastAPI
from pydantic import BaseModel

class Image(BaseModel):
    url: str
    name: str

class Item(BaseModel):
    name: str
    price: float
    tags: set[str] = set()
    images: list[Image] | None = None

app = FastAPI()

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    return {"item_id": item_id, "item": item.model_dump()}

这回 images 是一组图片对象,请求体类似:

{
    "name": "Foo",
    "price": 42.0,
    "images": [
        { "url": "http://example.com/baz.jpg", "name": "图一" },
        { "url": "http://example.com/dave.jpg", "name": "图二" }
    ]
}

FastAPI 会逐个校验数组里的每个对象,少一个字段、类型不对都会报错。

12-6 更深层的嵌套

嵌套没有层数限制。你可以一层套一层,把真实业务的结构完整画出来。

from fastapi import FastAPI
from pydantic import BaseModel

class Image(BaseModel):
    url: str
    name: str

class Item(BaseModel):
    name: str
    price: float
    images: list[Image] | None = None

class Offer(BaseModel):
    name: str
    price: float
    items: list[Item]

app = FastAPI()

@app.post("/offers/")
async def create_offer(offer: Offer):
    return offer

这里 Offer 包含一组 Item,而每个 Item 又可能带一组 Image。三层结构,声明方式却一样简单。FastAPI 会递归校验到底,文档也会层层展开。

Tip

写深层模型时,先定义最底层的子模型(如 Image),再往上组合。这样思路清晰,也方便复用同一个子模型。

12-7 给 URL 字段用专用类型

如果字段存的是网址,用普通 str 也能跑。但 Pydantic 提供了更专业的 HttpUrl 类型,会校验「这到底是不是合法网址」。

from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl

class Image(BaseModel):
    url: HttpUrl
    name: str

class Item(BaseModel):
    name: str
    price: float
    image: Image | None = None

app = FastAPI()

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    return {"item_id": item_id, "item": item.model_dump()}

客户端传了非法网址,Pydantic 直接拒绝;传了合法网址,它还会在文档里标注成 URL 类型。这种专业类型能挡掉不少脏数据。

12-8 纯列表的请求体

有时接口顶层不是对象,而是一个数组。比如「批量上传一组图片」,请求体直接就是 [{...}, {...}]

这种情况,把参数类型声明成 list[Image] 即可:

from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl

class Image(BaseModel):
    url: HttpUrl
    name: str

app = FastAPI()

@app.post("/images/multiple/")
async def create_multiple_images(images: list[Image]):
    return images

FastAPI 会把整个数组按 Image 模型逐个校验。注意这时没有外层字段名,数组本身就是请求体。

12-9 嵌套响应模型

把嵌套模型当作返回值时,FastAPI 会自动把 Python 对象序列化成嵌套的 JSON。你不用手动拼字典。

比如上面 create_offer 返回 offer,响应会自动变成带 items 数组、数组里再带 images 的多层 JSON。输入怎么校验,输出就怎么转换,全程对称。

这也是用 Pydantic 的一大好处:请求进来校验一遍,响应出去序列化一遍,模型定义只写一次,两端都受益。

12-10 小结

嵌套模型让你描述真实世界的复杂数据,关键记住:

  • list[str] 表达字符串列表,set[str] 自动去重。
  • 模型字段的类型可以是一个 Pydantic 模型,形成嵌套对象。
  • list[Image] 表示「一组对象」,数组里的每个元素都会被校验。
  • 嵌套校验、文档、序列化全自动,深度不限。
  • 网址等字段可用 HttpUrl 等专用类型做更准的校验。
  • 返回嵌套模型时,FastAPI 自动序列化成多层 JSON。

嵌套模型是组织复杂数据的利器。下一章我们看另一种数据形式:表单和文件上传。