输入和输出的独立OpenAPI模式或不独立¶
在使用 Pydantic v2 时,生成的OpenAPI比以前更加精确和**正确**。😎
事实上,在某些情况下,它甚至会为同一个Pydantic模型生成**两个JSON模式**,一个用于输入,一个用于输出,这取决于它们是否具有**默认值**。
让我们看看这是如何工作的,以及如果你需要这样做时如何更改它。
输入和输出的Pydantic模型¶
假设你有一个带有默认值的Pydantic模型,像这样:
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
# 下面的代码省略了 👇
👀 完整文件预览
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
# 下面的代码省略了 👇
👀 完整文件预览
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
# 下面的代码省略了 👇
👀 完整文件预览
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
# 下面的代码省略了 👇
👀 完整文件预览
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
# 下面的代码省略了 👇
👀 完整文件预览
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
# 下面的代码省略了 👇
👀 完整文件预览
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模式可能会根据它是用于**输入还是输出**而有所不同:
- 对于**输入**,
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"),
]
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 中的行为相同。🤓