输入和输出是否使用独立的 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 typing import List, Union
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: Union[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"),
]
🤓 其他版本和变体
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 typing import List, Union
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: Union[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"),
]
🤓 其他版本和变体
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 typing import List, Union
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: Union[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 Schema 可能会有所不同,具体取决于它是用于**输入还是输出**
- 对于**输入**,`description` 将**不是必需的**
- 对于**输出**,它将是**必需的**(并且可能为 `None`,或者在 JSON 中为 `null`)
文档中的输出模型¶
你也可以在文档中检查输出模型,`name` 和 `description` **两者**都被标记为**必需的**并带有**红色星号**

文档中的输入和输出模型¶
如果你检查 OpenAPI 中所有可用的 Schema(JSON Schema),你会看到有两个,一个 `Item-Input` 和一个 `Item-Output`。
对于 `Item-Input`,`description` **不是必需的**,它没有红色星号。
但对于 `Item-Output`,`description` 是**必需的**,它有红色星号。

借助 **Pydantic v2** 的此功能,你的 API 文档将更**精确**,如果你有自动生成的客户端和 SDK,它们也将更精确,具有更好的**开发者体验**和一致性。🎉
不分离 Schema¶
现在,在某些情况下,你可能希望输入和输出使用**相同的 Schema**。
这种情况的主要用例可能是,你已经有一些自动生成的客户端代码/SDK,并且你暂时不想更新所有这些自动生成的客户端代码/SDK,你可能最终会想更新,但可能不是现在。
在这种情况下,你可以通过参数 `separate_input_output_schemas=False` 在 **FastAPI** 中禁用此功能。
信息
对 `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"),
]
from typing import List, Union
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: Union[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"),
]
文档中输入和输出模型使用相同的 Schema¶
现在模型将只有一个用于输入和输出的 Schema,即 `Item`,并且它的 `description` 将**不是必需的**

这与 Pydantic v1 中的行为相同。🤓