请求体 - 嵌套模型¶
借助 FastAPI,您可以定义、校验、文档化和使用任意深度嵌套的模型(感谢 Pydantic)。
列表字段¶
您可以将一个属性定义为某种子类型。例如,Python 的 list。
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: list = []
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
🤓 其他版本和变体
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: list = []
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
这会使 tags 成为一个列表,但它没有声明该列表中元素的类型。
带有类型参数的列表字段¶
但是 Python 有一种声明带有内部类型或“类型参数”的列表的特定方式。
导入 typing 的 List¶
在 Python 3.9 及以上版本中,您可以使用标准的 list 来声明这些类型注解,我们将在下面看到。💡
但在 3.9 之前的 Python 版本(3.6 及以上)中,您需要先从标准 Python 的 typing 模块中导入 List。
from typing import List, Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: List[str] = []
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
🤓 其他版本和变体
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: list[str] = []
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: list[str] = []
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
声明一个带类型参数的 list¶
要声明带有类型参数(内部类型)的类型,例如 list、dict、tuple:
- 如果您使用的 Python 版本低于 3.9,请从
typing模块导入它们的等效版本。 - 使用方括号
[和]将内部类型作为“类型参数”传入:
在 Python 3.9 中,它将是:
my_list: list[str]
在 3.9 之前的 Python 版本中,它将是:
from typing import List
my_list: List[str]
这都是用于类型声明的标准 Python 语法。
对于带有内部类型的模型属性,也使用相同的标准语法。
因此,在我们的例子中,我们可以将 tags 明确指定为一个“字符串列表”:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: list[str] = []
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
🤓 其他版本和变体
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: list[str] = []
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
from typing import List, Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: List[str] = []
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
集合类型¶
但后来我们想了想,意识到标签不应该重复,它们可能应该是唯一的字符串。
而 Python 有一种用于存储唯一项集合的特殊数据类型,即 set。
那么我们可以将 tags 声明为一个字符串集合:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
🤓 其他版本和变体
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: set[str] = set()
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
from typing import Set, Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: Set[str] = set()
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
这样,即使您收到包含重复数据的请求,它也会被转换为一个包含唯一项的集合。
而且当您输出这些数据时,即使源数据有重复,它也会以唯一项的集合形式输出。
并且它也会被相应地注解和文档化。
嵌套模型¶
Pydantic 模型的每个属性都有一个类型。
但这个类型本身也可以是另一个 Pydantic 模型。
因此,您可以声明具有特定属性名称、类型和校验的深度嵌套的 JSON“对象”。
所有这些,都可以任意嵌套。
定义子模型¶
例如,我们可以定义一个 Image 模型:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Image(BaseModel):
url: str
name: str
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()
image: Image | None = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
🤓 其他版本和变体
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Image(BaseModel):
url: str
name: str
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: set[str] = set()
image: Union[Image, None] = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
from typing import Set, Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Image(BaseModel):
url: str
name: str
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: Set[str] = set()
image: Union[Image, None] = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
使用子模型作为类型¶
然后我们可以用它作为某个属性的类型:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Image(BaseModel):
url: str
name: str
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()
image: Image | None = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
🤓 其他版本和变体
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Image(BaseModel):
url: str
name: str
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: set[str] = set()
image: Union[Image, None] = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
from typing import Set, Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Image(BaseModel):
url: str
name: str
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: Set[str] = set()
image: Union[Image, None] = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
这意味着 FastAPI 将期望一个类似这样的请求体:
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2,
"tags": ["rock", "metal", "bar"],
"image": {
"url": "http://example.com/baz.jpg",
"name": "The Foo live"
}
}
同样,只需进行这样的声明,您就可以通过 FastAPI 获得:
- 编辑器支持(代码补全等),即使对于嵌套模型也同样有效
- 数据转换
- 数据验证
- 自动文档
特殊类型和校验¶
除了像 str、int、float 等普通的单一类型外,您还可以使用更复杂的、继承自 str 的单一类型。
要查看所有可用选项,请查阅 Pydantic 的类型概览。您将在下一章中看到一些示例。
例如,在 Image 模型中,我们有一个 url 字段,我们可以将其声明为 Pydantic 的 HttpUrl 实例,而不是一个 str:
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl
app = FastAPI()
class Image(BaseModel):
url: HttpUrl
name: str
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()
image: Image | None = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
🤓 其他版本和变体
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl
app = FastAPI()
class Image(BaseModel):
url: HttpUrl
name: str
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: set[str] = set()
image: Union[Image, None] = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
from typing import Set, Union
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl
app = FastAPI()
class Image(BaseModel):
url: HttpUrl
name: str
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: Set[str] = set()
image: Union[Image, None] = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
该字符串将被检查是否为有效的 URL,并在 JSON Schema / OpenAPI 中进行相应地文档化。
含有子模型列表的属性¶
您也可以将 Pydantic 模型用作 list、set 等的子类型。
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl
app = FastAPI()
class Image(BaseModel):
url: HttpUrl
name: str
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()
images: list[Image] | None = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
🤓 其他版本和变体
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl
app = FastAPI()
class Image(BaseModel):
url: HttpUrl
name: str
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: set[str] = set()
images: Union[list[Image], None] = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
from typing import List, Set, Union
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl
app = FastAPI()
class Image(BaseModel):
url: HttpUrl
name: str
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: Set[str] = set()
images: Union[List[Image], None] = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
这将期望(转换、校验、文档化等)一个类似这样的 JSON 请求体:
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2,
"tags": [
"rock",
"metal",
"bar"
],
"images": [
{
"url": "http://example.com/baz.jpg",
"name": "The Foo live"
},
{
"url": "http://example.com/dave.jpg",
"name": "The Baz"
}
]
}
信息
注意 images 键现在对应一个图像对象列表。
深度嵌套模型¶
您可以定义任意深度嵌套的模型:
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl
app = FastAPI()
class Image(BaseModel):
url: HttpUrl
name: str
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()
images: list[Image] | None = None
class Offer(BaseModel):
name: str
description: str | None = None
price: float
items: list[Item]
@app.post("/offers/")
async def create_offer(offer: Offer):
return offer
🤓 其他版本和变体
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl
app = FastAPI()
class Image(BaseModel):
url: HttpUrl
name: str
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: set[str] = set()
images: Union[list[Image], None] = None
class Offer(BaseModel):
name: str
description: Union[str, None] = None
price: float
items: list[Item]
@app.post("/offers/")
async def create_offer(offer: Offer):
return offer
from typing import List, Set, Union
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl
app = FastAPI()
class Image(BaseModel):
url: HttpUrl
name: str
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: Set[str] = set()
images: Union[List[Image], None] = None
class Offer(BaseModel):
name: str
description: Union[str, None] = None
price: float
items: List[Item]
@app.post("/offers/")
async def create_offer(offer: Offer):
return offer
信息
注意 Offer 包含一个 Item 列表,而 Item 又包含一个可选的 Image 列表。
纯列表构成的请求体¶
如果您期望的 JSON 请求体的顶层值是一个 JSON array(一个 Python list),您可以在函数的参数中声明类型,就像在 Pydantic 模型中一样:
images: List[Image]
或者在 Python 3.9及以上版本中:
images: list[Image]
例如:
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl
app = FastAPI()
class Image(BaseModel):
url: HttpUrl
name: str
@app.post("/images/multiple/")
async def create_multiple_images(images: list[Image]):
return images
🤓 其他版本和变体
from typing import List
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl
app = FastAPI()
class Image(BaseModel):
url: HttpUrl
name: str
@app.post("/images/multiple/")
async def create_multiple_images(images: List[Image]):
return images
处处皆可用的编辑器支持¶
而且您在任何地方都能获得编辑器支持。
即使对于列表中的项也一样:

如果您直接使用 dict 而不是 Pydantic 模型,就无法获得这种编辑器支持。
但您也不必担心它们,传入的字典会自动转换,您的输出也会自动转换为 JSON。
任意 dict 构成的请求体¶
您还可以将请求体声明为一个 dict,其键为某种类型,值为另一种类型。
这样,您就不必事先知道有效的字段/属性名称是什么(而使用 Pydantic 模型则需要)。
如果您想接收您尚不知道的键,这将非常有用。
另一个有用的情况是,当您希望键是另一种类型时(例如 int)。
这正是我们在这里要看到的。
在这种情况下,您将接受任何 dict,只要它的键是 int 类型,值是 float 类型:
from fastapi import FastAPI
app = FastAPI()
@app.post("/index-weights/")
async def create_index_weights(weights: dict[int, float]):
return weights
🤓 其他版本和变体
from typing import Dict
from fastapi import FastAPI
app = FastAPI()
@app.post("/index-weights/")
async def create_index_weights(weights: Dict[int, float]):
return weights
提示
请记住,JSON 只支持 str 作为键。
但是 Pydantic 具有自动数据转换功能。
这意味着,即使您的 API 客户端只能发送字符串作为键,只要这些字符串包含纯整数,Pydantic 就会对它们进行转换和校验。
而您作为 weights 接收到的 dict 将实际拥有 int 类型的键和 float 类型的值。
总结¶
借助 FastAPI,您可以获得 Pydantic 模型提供的最大灵活性,同时保持代码的简洁、短小和优雅。
并且享有所有好处:
- 编辑器支持(处处都有代码补全!)
- 数据转换(即解析/序列化)
- 数据验证
- 模式文档
- 自动文档