1 多个参数
1.1 混合使用 Path
、Query
和请求体参数
首先,毫无疑问地,你可以随意地混合使用 Path
、Query
和请求体参数声明,FastAPI 会知道该如何处理。你还可以通过将默认值设置为 None
来将请求体参数声明为可选参数:
from typing import Annotatedfrom fastapi import FastAPI, Path
from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strdescription: str | None = Noneprice: floattax: float | None = None@app.put("/items/{item_id}")
async def update_item(item_id: Annotated[int, Path(title="The ID of the item to get", ge=0, le=1000)],q: str | None = None,item: Item | None = None,
):results = {"item_id": item_id}if q:results.update({"q": q})if item:results.update({"item": item})return results
请注意,在这种情况下,将从请求体获取的 item
是可选的。因为它的默认值为 None
。
1.2 多个请求体参数
在上面的示例中,路径操作将期望一个具有 Item
的属性的 JSON 请求体,就像:
{"name": "Foo","description": "The pretender","price": 42.0,"tax": 3.2
}
但是你也可以声明多个请求体参数,例如 item
和 user
:
from fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strdescription: str | None = Noneprice: floattax: float | None = Noneclass User(BaseModel):username: strfull_name: str | None = None@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, user: User):results = {"item_id": item_id, "item": item, "user": user}return results
在这种情况下,FastAPI 将注意到该函数中有多个请求体参数(两个 Pydantic 模型参数)。因此,它将使用参数名称作为请求体中的键(字段名称),并期望一个类似于以下内容的请求体:
{"item": {"name": "Foo","description": "The pretender","price": 42.0,"tax": 3.2},"user": {"username": "dave","full_name": "Dave Grohl"}
}
FastAPI 将自动对请求中的数据进行转换,因此 item
参数将接收指定的内容,user
参数也是如此。它将执行对复合数据的校验,并且像现在这样为 OpenAPI 模式和自动化文档对其进行记录。
1.3 请求体中的单一值
与使用 Query
和 Path
为查询参数和路径参数定义额外数据的方式相同,FastAPI 提供了一个同等的 Body
。例如,为了扩展先前的模型,你可能决定除了 item
和 user
之外,还想在同一请求体中具有另一个键 importance
。
如果你就按原样声明它,因为它是一个单一值,FastAPI 将假定它是一个查询参数。但是你可以使用 Body
指示 FastAPI 将其作为请求体的另一个键进行处理。
from typing import Annotatedfrom fastapi import Body, FastAPI
from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strdescription: str | None = Noneprice: floattax: float | None = Noneclass User(BaseModel):username: strfull_name: str | None = None@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, user: User, importance: Annotated[int, Body()]
):results = {"item_id": item_id, "item": item, "user": user, "importance": importance}return results
在这种情况下,FastAPI 将期望像这样的请求体:
{"item": {"name": "Foo","description": "The pretender","price": 42.0,"tax": 3.2},"user": {"username": "dave","full_name": "Dave Grohl"},"importance": 5
}
同样的,它将转换数据类型,校验,生成文档等。
1.4 多个请求体参数和查询参数
当然,除了请求体参数外,你还可以在任何需要的时候声明额外的查询参数。由于默认情况下单一值被解释为查询参数,因此你不必显式地添加 Query
,你可以仅执行以下操作:
q: str = None
@app.put("/items/{item_id}")
async def update_item(*,item_id: int,item: Item,user: User,importance: Annotated[int, Body(gt=0)],q: str | None = None,
):
Body
同样具有与 Query
、Path
以及其他后面将看到的类完全相同的额外校验和元数据参数。
1.5 嵌入单个请求体参数
假设你只有一个来自 Pydantic 模型 Item
的请求体参数 item
。默认情况下,FastAPI 将直接期望这样的请求体。但是,如果你希望它期望一个拥有 item
键并在值中包含模型内容的 JSON,就像在声明额外的请求体参数时所做的那样,则可以使用一个特殊的 Body
参数 embed
:
item: Item = Body(embed=True)
from typing import Annotatedfrom fastapi import Body, FastAPI
from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strdescription: str | None = Noneprice: floattax: float | None = None@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]):results = {"item_id": item_id, "item": item}return results
在这种情况下,FastAPI 将期望像这样的请求体:
{"item": {"name": "Foo","description": "The pretender","price": 42.0,"tax": 3.2}
}
而不是:
{"name": "Foo","description": "The pretender","price": 42.0,"tax": 3.2
}
2 字段
与在路径操作函数中使用 Query
、Path
、Body
声明校验与元数据的方式一样,可以使用 Pydantic 的 Field
在 Pydantic 模型内部声明校验和元数据。首先,从 Pydantic 中导入 Field
:
from typing import Annotatedfrom fastapi import Body, FastAPI
from pydantic import BaseModel, Field
然后,使用 Field
定义模型的属性:
class Item(BaseModel):name: strdescription: str | None = Field(default=None, title="The description of the item", max_length=300)price: float = Field(gt=0, description="The price must be greater than zero")tax: float | None = None@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]):results = {"item_id": item_id, "item": item}return results
模型属性的类型、默认值及 Field
的代码结构与路径操作函数的参数相同,只不过是用 Field
替换了Path
、Query
、Body
。
3 嵌套模型
使用 FastAPI,你可以定义、校验、记录文档并使用任意深度嵌套的模型(归功于Pydantic)。
3.1 List 字段
你可以将一个属性定义为拥有子元素的类型。例如 Python list
:
class Item(BaseModel):name: strdescription: str | None = Noneprice: floattax: float | None = Nonetags: list = []
这将使 tags
成为一个由元素组成的列表。不过它没有声明每个元素的类型。Python 有一种特定的方法来声明具有子类型的列表:
from typing import List, Unionfrom fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strdescription: Union[str, None] = Noneprice: floattax: Union[float, None] = Nonetags: List[str] = []
这完全是用于类型声明的标准 Python 语法。对具有子类型的模型属性也使用相同的标准语法。因此,在我们的示例中,我们可以将 tags
明确地指定为一个「字符串列表」。
3.2 Set 类型
Python 具有一种特殊的数据类型来保存一组唯一的元素,即 set
。然后我们可以导入 Set
并将 tag
声明为一个由 str
组成的 set
:
class Item(BaseModel):name: strdescription: str | None = Noneprice: floattax: float | None = Nonetags: set[str] = set()
这样,即使你收到带有重复数据的请求,这些数据也会被转换为一组唯一项。而且,每当你输出该数据时,即使源数据有重复,它们也将作为一组唯一项输出。并且还会被相应地标注 / 记录文档。
3.3 嵌套模型
Pydantic 模型的每个属性都具有类型。但是这个类型本身可以是另一个 Pydantic 模型。因此,你可以声明拥有特定属性名称、类型和校验的深度嵌套的 JSON 对象。上述这些都可以任意的嵌套。例如,我们可以定义一个 Image
模型:
class Image(BaseModel):url: strname: str
然后我们可以将其用作一个属性的类型:
class Item(BaseModel):name: strdescription: str | None = Noneprice: floattax: float | None = Nonetags: set[str] = set()image: Image | None = None
这意味着 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 获得:
- 对被嵌入的模型也适用的编辑器支持(自动补全等)
- 数据转换
- 数据校验
- 自动生成文档
3.4 特殊的类型和校验
除了普通的单一值类型(如 str
、int
、float
等)外,你还可以使用从 str
继承的更复杂的单一值类型。例如,在 Image
模型中我们有一个 url
字段,我们可以把它声明为 Pydantic 的 HttpUrl
,而不是 str
:
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrlapp = FastAPI()class Image(BaseModel):url: HttpUrlname: str
该字符串将被检查是否为有效的 URL,并在 JSON Schema / OpenAPI 文档中进行记录。
3.5 带有一组子模型的属性
你还可以将 Pydantic 模型用作 list
、set
等的子类型:
class Image(BaseModel):url: HttpUrlname: strclass Item(BaseModel):name: strdescription: str | None = Noneprice: floattax: float | None = Nonetags: set[str] = set()images: list[Image] | None = None
这将期望(转换,校验,记录文档等)下面这样的 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"}]
}
3.6 深度嵌套模型
你可以定义任意深度的嵌套模型:
class Image(BaseModel):url: HttpUrlname: strclass Item(BaseModel):name: strdescription: str | None = Noneprice: floattax: float | None = Nonetags: set[str] = set()images: list[Image] | None = Noneclass Offer(BaseModel):name: strdescription: str | None = Noneprice: floatitems: list[Item]
3.7 纯列表请求体
如果你期望的 JSON 请求体的最外层是一个 JSON array
(即 Python list
),则可以在路径操作函数的参数中声明此类型,就像声明 Pydantic 模型一样:
images: List[Image]
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrlapp = FastAPI()class Image(BaseModel):url: HttpUrlname: str@app.post("/images/multiple/")
async def create_multiple_images(images: list[Image]):return images