入出力でOpenAPIスキーマを分離するかどうか¶
Pydantic v2を使用すると、生成されるOpenAPIは以前よりも少し正確で正しくなります。 😎
実際、場合によっては、デフォルト値があるかどうかによって、入力と出力用に、同じPydanticモデルに対してOpenAPIに2つの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 typing import Optional
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: Optional[str] = None
# Code below omitted 👇
👀 ファイルの完全なプレビュー
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
# Code below omitted 👇
👀 ファイルの完全なプレビュー
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
# Code below omitted 👇
👀 ファイルの完全なプレビュー
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
# Code below omitted 👇
👀 ファイルの完全なプレビュー
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
フィールドの1つに何も追加していない場合でも、JSONレスポンスにはデフォルト値(null
)が含まれています。

これは、常に値を持つことを意味します。ただし、値がNone
(またはJSONではnull
)の場合があるだけです。
つまり、APIを使用するクライアントは、値が存在するかどうかを確認する必要はなく、フィールドは常に存在すると想定できますが、場合によってはNone
のデフォルト値を持つだけです。
これをOpenAPIで記述する方法は、そのフィールドが常に存在するため、requiredとマークすることです。
そのため、モデルのJSONスキーマは、入力用か出力用かによって異なる場合があります。
- 入力の場合、
description
は必須ではありません。 - 出力の場合、
description
は必須です(場合によってはNone
、JSON用語ではnull
)。
ドキュメントにおける出力用モデル¶
ドキュメントの出力モデルを確認すると、name
とdescription
の両方が赤いアスタリスクで必須とマークされていることがわかります。

ドキュメントにおける入力および出力用モデル¶
OpenAPIで利用可能なすべてのスキーマ(JSONスキーマ)を確認すると、Item-Input
とItem-Output
の2つがあることがわかります。
Item-Input
の場合、description
は必須ではありません。赤いアスタリスクはありません。
しかし、Item-Output
の場合、description
は必須であり、赤いアスタリスクが付いています。

Pydantic v2のこの機能により、APIドキュメントがより正確になり、自動生成されたクライアントとSDKがある場合、それらもより正確になり、より優れた開発者体験と一貫性が得られます。🎉
スキーマを分離しない¶
さて、入力と出力に同じスキーマを使用したい場合もあります。
おそらく、これの主なユースケースは、すでに自動生成されたクライアントコード/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"),
]
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と同じ動作です。🤓