是否为输入和输出使用独立的 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 👇
👀 Full file preview
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"),
]
🤓 Other versions and variants
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 👇
👀 Full file preview
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"),
]
🤓 Other versions and variants
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"),
]
🤓 Other versions and variants
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 模式可能会有所不同:
- 对于输入,
description将不是必需的 - 对于输出,它将是必需的(并且可能是
None,或者在 JSON 术语中为null)
文档中的输出模型¶
你也可以在文档中检查输出模型,两个 name 和 description 都被标记为必需,并带有红色星号:
文档中的输入和输出模型¶
如果你检查 OpenAPI 中所有可用的模式(JSON 模式),你会看到有两个,一个是 Item-Input,一个是 Item-Output。
对于 Item-Input,description 不是必需的,它没有红色星号。
但对于 Item-Output,description 是必需的,它有红色星号。
借助 Pydantic v2 的这一特性,你的 API 文档更加精确,如果你有自动生成的客户端和 SDK,它们也会更加精确,具有更好的开发人员体验和一致性。🎉
不分离模式¶
现在,在某些情况下,你可能希望输入和输出使用相同的模式。
可能的主要用例是,如果你已经有一些自动生成的客户端代码/SDK,并且你不想更新所有自动生成的客户端代码/SDK,你可能希望在某个时候进行更新,但不是现在。
在这种情况下,你可以在 FastAPI 中禁用此功能,使用参数 separate_input_output_schemas=False。
Info
对 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"),
]
🤓 Other versions and variants
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"),
]
文档中输入和输出模型的相同模式¶
现在,模型的输入和输出将有一个单一的模式,只有 Item,并且 description 将不是必需的:
这与 Pydantic v1 中的行为相同。🤓