嵌套模型 Nested Models
本教程共 50 篇 · 第 12 篇 · 更新于 2026-08-12 · 约 7 分钟阅读
本节目标:学会在 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 对象,里面有 url 和 name。只要这样声明,FastAPI 就会自动校验内层结构,连 image.url 是不是字符串都帮你查。
12-4 嵌套结构自动校验
嵌套模型最省心的地方,是校验会一层层自动进行。你不用写任何额外代码。
- 外层
Item的name是必填字符串,price是必填数字。 - 内层
Image的url、name也按自己的类型校验。 - 哪一层出错,报错里的
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。
嵌套模型是组织复杂数据的利器。下一章我们看另一种数据形式:表单和文件上传。