跳到内容

是否为输入和输出分离 OpenAPI 架构

Pydantic v2 发布以来,生成的 OpenAPI 比以往更加精确且正确。😎

事实上,在某些情况下,对于同一个 Pydantic 模型,OpenAPI 中甚至会有两个 JSON 架构(分别用于输入和输出),具体取决于它们是否具有默认值

让我们看看它是如何工作的,以及如果你需要的话,该如何更改它。

用于输入和输出的 Pydantic 模型

假设你有一个带有默认值的 Pydantic 模型,如下所示

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: str | None = None

# Code below omitted 👇
👀 完整文件预览
from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: str | None = None


app = FastAPI()


@app.post("/items/")
def create_item(item: Item):
    return item


@app.get("/items/")
def read_items() -> list[Item]:
    return [
        Item(
            name="Portal Gun",
            description="Device to travel through the multi-rick-verse",
        ),
        Item(name="Plumbus"),
    ]

输入模型

如果你像下面这样将此模型用作输入

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: str | None = None


app = FastAPI()


@app.post("/items/")
def create_item(item: Item):
    return item

# Code below omitted 👇
👀 完整文件预览
from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: str | None = None


app = FastAPI()


@app.post("/items/")
def create_item(item: Item):
    return item


@app.get("/items/")
def read_items() -> list[Item]:
    return [
        Item(
            name="Portal Gun",
            description="Device to travel through the multi-rick-verse",
        ),
        Item(name="Plumbus"),
    ]

……那么 description 字段将不是必需的。因为它有一个默认值 None

文档中的输入模型

你可以在文档中确认,description 字段没有红色星号,它未被标记为必需项。

输出模型

但如果你将同一个模型用作输出,如下所示

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: str | None = None


app = FastAPI()


@app.post("/items/")
def create_item(item: Item):
    return item


@app.get("/items/")
def read_items() -> list[Item]:
    return [
        Item(
            name="Portal Gun",
            description="Device to travel through the multi-rick-verse",
        ),
        Item(name="Plumbus"),
    ]

……那么因为 description 有一个默认值,如果你没有为该字段返回任何内容,它仍然会保留该默认值

输出响应数据模型

如果你与文档交互并检查响应,即使代码没有在 description 字段中添加任何内容,JSON 响应也会包含默认值 (null)。

这意味着它总是会有一个值,只是有时这个值可能是 None(或者 JSON 中的 null)。

这意味着使用你 API 的客户端不必检查该值是否存在,他们可以假定该字段总是存在,只是在某些情况下它会有默认值 None

在 OpenAPI 中描述这种情况的方法是,将该字段标记为必需,因为它总是会存在。

因此,模型的 JSON 架构可能会根据它是用于输入还是输出而有所不同。

  • 对于输入description 不是必需的
  • 对于输出,它是必需的(并且可能为 None,即 JSON 术语中的 null

文档中的输出模型

你也可以在文档中检查输出模型,namedescription 都被标记为必需,带有红色星号

文档中的输入和输出模型

如果你检查 OpenAPI 中所有可用的架构(JSON 架构),你会看到有两个,一个是 Item-Input,一个是 Item-Output

对于 Item-Inputdescription 不是必需的,它没有红色星号。

但对于 Item-Outputdescription 是必需的,它有一个红色星号。

有了 Pydantic v2 的这一特性,你的 API 文档更加精确。如果你有自动生成的客户端和 SDK,它们也会更精确,从而提供更好的开发者体验和一致性。🎉

不分离架构

现在,在某些情况下,你可能希望对输入和输出使用相同的架构

这方面的主要用例可能是你已经有一些自动生成的客户端代码/SDK,并且目前不想更新所有这些内容。你可能最终还是会更新它们,但也许不是现在。

在这种情况下,你可以通过参数 separate_input_output_schemas=FalseFastAPI 中禁用此功能。

信息

FastAPI 0.102.0 中增加了对 separate_input_output_schemas 的支持。🤓

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: str | None = None


app = FastAPI(separate_input_output_schemas=False)


@app.post("/items/")
def create_item(item: Item):
    return item


@app.get("/items/")
def read_items() -> list[Item]:
    return [
        Item(
            name="Portal Gun",
            description="Device to travel through the multi-rick-verse",
        ),
        Item(name="Plumbus"),
    ]

文档中输入和输出使用相同模型架构

现在,该模型将只有一个用于输入和输出的单一架构,即 Item,并且 description 将被标记为非必需