请求体 - 嵌套模型¶
使用 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` 来声明这些类型注解,如下所示。💡
但在 Python 3.9 之前的版本(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]
在 Python 3.9 之前的版本中,将是
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 模型,就无法获得这种编辑器支持。
但你也不必担心它们,传入的 dict 会自动转换,你的输出也会自动转换为 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 模型提供的最大灵活性,同时保持你的代码简洁、短小且优雅。
但拥有所有这些好处
- 编辑器支持(无处不在的自动补全!)
- 数据转换(又称解析/序列化)
- 数据验证
- 模式文档
- 自动文档