リクエストのサンプルデータを宣言する¶
アプリが受信できるデータの例を宣言できます。
これを行うにはいくつかの方法があります。
Pydanticモデルの追加のJSONスキーマデータ¶
生成されたJSONスキーマに追加されるPydanticモデルのexamples
を宣言できます。
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
model_config = {
"json_schema_extra": {
"examples": [
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
]
}
}
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
class Config:
schema_extra = {
"examples": [
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
]
}
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
model_config = {
"json_schema_extra": {
"examples": [
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
]
}
}
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
class Config:
schema_extra = {
"examples": [
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
]
}
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
この追加情報は、そのモデルの出力JSONスキーマにそのまま追加され、APIドキュメントで使用されます。
Pydanticバージョン2では、Pydanticのドキュメント:構成で説明されているように、dict
を受け取る属性model_config
を使用します。
"json_schema_extra"
に、生成されたJSONスキーマに表示させたい追加データ(examples
を含む)を含むdict
を設定できます。
Pydanticバージョン1では、Pydanticのドキュメント:スキーマのカスタマイズで説明されているように、内部クラスConfig
とschema_extra
を使用します。
schema_extra
に、生成されたJSONスキーマに表示させたい追加データ(examples
を含む)を含むdict
を設定できます。
ヒント
同じ手法を使用してJSONスキーマを拡張し、独自のカスタム追加情報を追加することもできます。
たとえば、フロントエンドユーザーインターフェイスのメタデータを追加するために使用できます。
情報
OpenAPI 3.1.0 (FastAPI 0.99.0 以降で使用) では、JSON Schema 標準の一部である examples
のサポートが追加されました。
それ以前は、単一の例を持つキーワード example
のみがサポートされていました。これは OpenAPI 3.1.0 でも引き続きサポートされていますが、非推奨であり、JSON Schema 標準の一部ではありません。そのため、example
を examples
に移行することをお勧めします。🤓
詳細については、このページの最後をご覧ください。
Field
の追加引数¶
Pydantic モデルで Field()
を使用する場合、追加の examples
を宣言することもできます。
from fastapi import FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
class Item(BaseModel):
name: str = Field(examples=["Foo"])
description: str | None = Field(default=None, examples=["A very nice Item"])
price: float = Field(examples=[35.4])
tax: float | None = Field(default=None, examples=[3.2])
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
class Item(BaseModel):
name: str = Field(examples=["Foo"])
description: Union[str, None] = Field(default=None, examples=["A very nice Item"])
price: float = Field(examples=[35.4])
tax: Union[float, None] = Field(default=None, examples=[3.2])
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
JSON Schema における examples
- OpenAPI¶
次のいずれかを使用する場合
Path()
Query()
Header()
Cookie()
Body()
Form()
File()
OpenAPI 内の JSON Schema に追加される追加情報とともに、examples
のグループを宣言することもできます。
Body
と examples
¶
ここでは、Body()
で想定されるデータの例を 1 つ含む examples
を渡します。
from typing import Annotated
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
@app.put("/items/{item_id}")
async def update_item(
item_id: int,
item: Annotated[
Item,
Body(
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
],
),
],
):
results = {"item_id": item_id, "item": item}
return results
from typing import Annotated, Union
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
@app.put("/items/{item_id}")
async def update_item(
item_id: int,
item: Annotated[
Item,
Body(
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
],
),
],
):
results = {"item_id": item_id, "item": item}
return results
from typing import Union
from fastapi import Body, FastAPI
from pydantic import BaseModel
from typing_extensions import Annotated
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
@app.put("/items/{item_id}")
async def update_item(
item_id: int,
item: Annotated[
Item,
Body(
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
],
),
],
):
results = {"item_id": item_id, "item": item}
return results
ヒント
可能な限り、Annotated
バージョンを使用することをお勧めします。
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
@app.put("/items/{item_id}")
async def update_item(
item_id: int,
item: Item = Body(
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
],
),
):
results = {"item_id": item_id, "item": item}
return results
ヒント
可能な限り、Annotated
バージョンを使用することをお勧めします。
from typing import Union
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
@app.put("/items/{item_id}")
async def update_item(
item_id: int,
item: Item = Body(
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
],
),
):
results = {"item_id": item_id, "item": item}
return results
ドキュメント UI の例¶
上記の方法のいずれかを使用すると、/docs
でこのように表示されます。
複数の examples
を持つ Body
¶
もちろん、複数の examples
を渡すこともできます。
from typing import Annotated
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
@app.put("/items/{item_id}")
async def update_item(
*,
item_id: int,
item: Annotated[
Item,
Body(
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
{
"name": "Bar",
"price": "35.4",
},
{
"name": "Baz",
"price": "thirty five point four",
},
],
),
],
):
results = {"item_id": item_id, "item": item}
return results
from typing import Annotated, Union
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
@app.put("/items/{item_id}")
async def update_item(
*,
item_id: int,
item: Annotated[
Item,
Body(
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
{
"name": "Bar",
"price": "35.4",
},
{
"name": "Baz",
"price": "thirty five point four",
},
],
),
],
):
results = {"item_id": item_id, "item": item}
return results
from typing import Union
from fastapi import Body, FastAPI
from pydantic import BaseModel
from typing_extensions import Annotated
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
@app.put("/items/{item_id}")
async def update_item(
*,
item_id: int,
item: Annotated[
Item,
Body(
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
{
"name": "Bar",
"price": "35.4",
},
{
"name": "Baz",
"price": "thirty five point four",
},
],
),
],
):
results = {"item_id": item_id, "item": item}
return results
ヒント
可能な限り、Annotated
バージョンを使用することをお勧めします。
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
@app.put("/items/{item_id}")
async def update_item(
*,
item_id: int,
item: Item = Body(
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
{
"name": "Bar",
"price": "35.4",
},
{
"name": "Baz",
"price": "thirty five point four",
},
],
),
):
results = {"item_id": item_id, "item": item}
return results
ヒント
可能な限り、Annotated
バージョンを使用することをお勧めします。
from typing import Union
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
@app.put("/items/{item_id}")
async def update_item(
*,
item_id: int,
item: Item = Body(
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
{
"name": "Bar",
"price": "35.4",
},
{
"name": "Baz",
"price": "thirty five point four",
},
],
),
):
results = {"item_id": item_id, "item": item}
return results
これを行うと、例はそのボディデータの内部 JSON Schema の一部になります。
ただし、この記事を書いている時点では、ドキュメント UI の表示を担当するツールである Swagger UI は、JSON Schema のデータに対して複数の例を表示することをサポートしていません。ただし、回避策については以下をお読みください。
OpenAPI 固有の examples
¶
JSON Schema が examples
をサポートする以前から、OpenAPI は examples
という別のフィールドをサポートしていました。
この OpenAPI 固有の examples
は、OpenAPI 仕様の別のセクションにあります。各 JSON Schema の内部ではなく、各パス操作の詳細にあります。
そして、Swagger UI はこの特定の examples
フィールドをしばらくの間サポートしていました。そのため、これを使用して、ドキュメント UI にさまざまな 例を表示できます。
この OpenAPI 固有のフィールド examples
の形状は、複数の例 (list
の代わりに) を持つ dict
であり、それぞれに OpenAPI にも追加される追加情報が含まれています。
これは、OpenAPI に含まれる各 JSON Schema の内部ではなく、パス操作に直接、外部に配置されます。
openapi_examples
パラメータの使用¶
FastAPI では、OpenAPI 固有の examples
を、次の openapi_examples
パラメータを使用して宣言できます。
Path()
Query()
Header()
Cookie()
Body()
Form()
File()
dict
のキーは各例を識別し、各値は別の dict
です。
examples
の各特定の例の dict
には、次のものを含めることができます。
summary
: 例の簡単な説明。description
: Markdown テキストを含めることができる長い説明。value
: これは、実際に表示される例 (例:dict
) です。externalValue
:value
の代替として、例を指す URL。ただし、これはvalue
ほど多くのツールでサポートされていない可能性があります。
次のように使用できます。
from typing import Annotated
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
@app.put("/items/{item_id}")
async def update_item(
*,
item_id: int,
item: Annotated[
Item,
Body(
openapi_examples={
"normal": {
"summary": "A normal example",
"description": "A **normal** item works correctly.",
"value": {
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
},
"converted": {
"summary": "An example with converted data",
"description": "FastAPI can convert price `strings` to actual `numbers` automatically",
"value": {
"name": "Bar",
"price": "35.4",
},
},
"invalid": {
"summary": "Invalid data is rejected with an error",
"value": {
"name": "Baz",
"price": "thirty five point four",
},
},
},
),
],
):
results = {"item_id": item_id, "item": item}
return results
from typing import Annotated, Union
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
@app.put("/items/{item_id}")
async def update_item(
*,
item_id: int,
item: Annotated[
Item,
Body(
openapi_examples={
"normal": {
"summary": "A normal example",
"description": "A **normal** item works correctly.",
"value": {
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
},
"converted": {
"summary": "An example with converted data",
"description": "FastAPI can convert price `strings` to actual `numbers` automatically",
"value": {
"name": "Bar",
"price": "35.4",
},
},
"invalid": {
"summary": "Invalid data is rejected with an error",
"value": {
"name": "Baz",
"price": "thirty five point four",
},
},
},
),
],
):
results = {"item_id": item_id, "item": item}
return results
from typing import Union
from fastapi import Body, FastAPI
from pydantic import BaseModel
from typing_extensions import Annotated
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
@app.put("/items/{item_id}")
async def update_item(
*,
item_id: int,
item: Annotated[
Item,
Body(
openapi_examples={
"normal": {
"summary": "A normal example",
"description": "A **normal** item works correctly.",
"value": {
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
},
"converted": {
"summary": "An example with converted data",
"description": "FastAPI can convert price `strings` to actual `numbers` automatically",
"value": {
"name": "Bar",
"price": "35.4",
},
},
"invalid": {
"summary": "Invalid data is rejected with an error",
"value": {
"name": "Baz",
"price": "thirty five point four",
},
},
},
),
],
):
results = {"item_id": item_id, "item": item}
return results
ヒント
可能な限り、Annotated
バージョンを使用することをお勧めします。
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
@app.put("/items/{item_id}")
async def update_item(
*,
item_id: int,
item: Item = Body(
openapi_examples={
"normal": {
"summary": "A normal example",
"description": "A **normal** item works correctly.",
"value": {
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
},
"converted": {
"summary": "An example with converted data",
"description": "FastAPI can convert price `strings` to actual `numbers` automatically",
"value": {
"name": "Bar",
"price": "35.4",
},
},
"invalid": {
"summary": "Invalid data is rejected with an error",
"value": {
"name": "Baz",
"price": "thirty five point four",
},
},
},
),
):
results = {"item_id": item_id, "item": item}
return results
ヒント
可能な限り、Annotated
バージョンを使用することをお勧めします。
from typing import Union
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
@app.put("/items/{item_id}")
async def update_item(
*,
item_id: int,
item: Item = Body(
openapi_examples={
"normal": {
"summary": "A normal example",
"description": "A **normal** item works correctly.",
"value": {
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
},
"converted": {
"summary": "An example with converted data",
"description": "FastAPI can convert price `strings` to actual `numbers` automatically",
"value": {
"name": "Bar",
"price": "35.4",
},
},
"invalid": {
"summary": "Invalid data is rejected with an error",
"value": {
"name": "Baz",
"price": "thirty five point four",
},
},
},
),
):
results = {"item_id": item_id, "item": item}
return results
ドキュメント UI での OpenAPI の例¶
openapi_examples
を Body()
に追加すると、/docs
は次のようになります。
技術的な詳細¶
ヒント
すでに FastAPI バージョン 0.99.0 以上を使用している場合は、おそらくこれらの詳細を スキップできます。
これらは、OpenAPI 3.1.0 が利用可能になる前の古いバージョンに関連しています。
これは、OpenAPI と JSON Schema の簡単な 歴史レッスン と考えてください。🤓
警告
これらは、標準の JSON Schema と OpenAPI に関する非常に技術的な詳細です。
上記のアイデアがすでに機能している場合は、それで十分な場合があり、おそらくこれらの詳細を知る必要はありません。自由にスキップしてください。
OpenAPI 3.1.0 より前は、OpenAPI は古い修正バージョンの JSON Schema を使用していました。
JSON Schema には examples
がなかったため、OpenAPI は独自の修正バージョンに独自の example
フィールドを追加しました。
OpenAPI は、仕様の他の部分にも example
および examples
フィールドを追加しました
Parameter Object
(仕様内) これは FastAPI のもので使用されていましたPath()
Query()
Header()
Cookie()
Request Body Object
、フィールドcontent
内、Media Type Object
(仕様内) これは FastAPI のもので使用されていましたBody()
File()
Form()
情報
この古い OpenAPI 固有の examples
パラメータは、FastAPI 0.103.0
以降、openapi_examples
になりました。
JSON Schema の examples
フィールド¶
しかしその後、JSON Schema は仕様の新しいバージョンに examples
フィールドを追加しました。
そして、新しい OpenAPI 3.1.0 は、この新しいフィールド examples
を含む最新バージョン (JSON Schema 2020-12) に基づいていました。
そして今、この新しい examples
フィールドは、非推奨になった古い単一の (カスタム) example
フィールドよりも優先されます。
JSON Schema のこの新しい examples
フィールドは、単なる list
であり、OpenAPI の他の場所 (上記) のように追加のメタデータを持つ dict ではありません。
情報
OpenAPI 3.1.0 が JSON Schema とのこの新しいシンプルな統合でリリースされた後も、しばらくの間、自動ドキュメントを提供するツールである Swagger UI は OpenAPI 3.1.0 をサポートしていませんでした (バージョン 5.0.0 以降はサポートしています🎉)。
そのため、0.99.0 より前の FastAPI のバージョンでは、3.1.0 より低いバージョンの OpenAPI が使用されていました。
Pydantic および FastAPI の examples
¶
schema_extra
または Field(examples=["something"])
を使用して、Pydantic モデル内に examples
を追加すると、その例は、その Pydantic モデルの JSON Schema に追加されます。
そして、その Pydantic モデルの JSON Schema は、API の OpenAPI に含まれ、ドキュメント UI で使用されます。
0.99.0 より前の FastAPI のバージョン (0.99.0 以上は新しい OpenAPI 3.1.0 を使用) で、他のユーティリティ (Query()
、Body()
など) で example
または examples
を使用すると、これらの例は、そのデータを記述する JSON Schema に追加されませんでした (OpenAPI 独自の JSON Schema のバージョンにも追加されませんでした)。それらは、OpenAPI のパス操作宣言に直接追加されました (JSON Schema を使用する OpenAPI の部分の外側)。
しかし、FastAPI 0.99.0 以上が JSON Schema 2020-12 を使用する OpenAPI 3.1.0 を使用するようになり、Swagger UI 5.0.0 以上になったことで、すべてがより一貫性があり、例は JSON Schema に含まれるようになりました。
Swagger UI と OpenAPI 固有の examples
¶
現在、Swagger UI は複数の JSON Schema の例をサポートしていないため (2023 年 8 月 26 日現在)、ユーザーはドキュメントに複数の例を表示する方法がありませんでした。
それを解決するために、FastAPI 0.103.0
は、新しいパラメータ openapi_examples
を使用して、同じ古い OpenAPI 固有の examples
フィールドを宣言する サポートを追加しました。🤓
まとめ¶
昔は歴史があまり好きではなかったと言っていましたが…今では「技術史」のレッスンをしています。😅
要するに、FastAPI 0.99.0 以上にアップグレードすれば、物事ははるかに シンプルで、一貫性があり、直感的になり、これらの歴史的な詳細をすべて知る必要はありません。😎