主体 - 嵌套模型¶
使用 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
声明带有类型参数的 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 模型,则无法获得这种编辑器支持。
但是您也不必担心它们,传入的字典会自动转换,您的输出也会自动转换为 JSON。
任意 dict
的主体¶
您还可以将主体声明为一个 dict
,其中键为某种类型,值则为另一种类型。
这样,您就不必事先知道有效的字段/属性名称(与 Pydantic 模型不同)。
如果您想要接收尚不知道的键,这将很有用。
另一个有用的情况是,当您想要使用其他类型的键(例如 int
)时。
这就是我们将在本文中看到的。
在这种情况下,只要 dict
具有 int
键和 float
值,您就可以接受任何 dict
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 模型提供的最大灵活性,同时保持代码简洁、短小且优雅。
但所有这些都有优势
- 编辑器支持(无处不在的代码补全!)
- 数据转换(又称解析/序列化)
- 数据验证
- 架构文档
- 自动文档