跳到内容

单独为输入和输出定义 OpenAPI Schema 或不定义

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

实际上,在某些情况下,同一个 Pydantic 模型在 OpenAPI 中会生成两个 JSON Schema,分别用于输入和输出,具体取决于它们是否具有默认值

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

用于输入和输出的 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 typing import Optional

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: Optional[str] = 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"),
    ]
🤓 其他版本和变体
from typing import Optional

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: Optional[str] = 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"),
    ]
🤓 其他版本和变体
from typing import Optional

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: Optional[str] = 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 Schema 可以根据它是用于输入还是输出而有所不同

  • 对于输入description不是必需的
  • 对于输出,它将是必需的 (并且可能是 None,或者用 JSON 的术语来说,是 null)

文档中的输出模型

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

文档中的输入和输出模型

如果你查看 OpenAPI 中所有可用的 Schema (JSON Schema),你会发现有两个,一个 Item-Input 和一个 Item-Output

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

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

通过 Pydantic v2 的这项功能,你的 API 文档将更加精确,如果你有自动生成的客户端和 SDK,它们也将更加精确,具有更好的开发者体验和一致性。🎉

不单独定义 Schema

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

这可能最主要的用例是你已经拥有一些自动生成的客户端代码/SDK,并且你不想立即更新所有自动生成的客户端代码/SDK,也许你将来会这样做,但现在不行。

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

信息

separate_input_output_schemas 的支持已添加到 FastAPI 0.102.0 中。🤓

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"),
    ]
🤓 其他版本和变体
from typing import Optional

from fastapi import FastAPI
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    description: Optional[str] = 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"),
    ]

文档中输入和输出模型相同的 Schema

现在,该模型将有一个用于输入和输出的单一 Schema,只有 Item,并且 description不是必需的