パス操作の高度な設定¶
OpenAPI operationId¶
警告
OpenAPI の「エキスパート」でない限り、これはおそらく必要ありません。
パス操作で使用される OpenAPI operationId
を、operation_id
パラメータで設定できます。
各操作に対して一意であることを確認する必要があります。
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/", operation_id="some_specific_id_you_define")
async def read_items():
return [{"item_id": "Foo"}]
パス操作関数名を operationId として使用する¶
API の関数名を operationId
として使用する場合、それらをすべて反復処理し、APIRoute.name
を使用して各パス操作の operation_id
をオーバーライドできます。
これは、すべてのパス操作を追加した後に実行する必要があります。
from fastapi import FastAPI
from fastapi.routing import APIRoute
app = FastAPI()
@app.get("/items/")
async def read_items():
return [{"item_id": "Foo"}]
def use_route_names_as_operation_ids(app: FastAPI) -> None:
"""
Simplify operation IDs so that generated API clients have simpler function
names.
Should be called only after all routes have been added.
"""
for route in app.routes:
if isinstance(route, APIRoute):
route.operation_id = route.name # in this case, 'read_items'
use_route_names_as_operation_ids(app)
ヒント
app.openapi()
を手動で呼び出す場合は、その前に operationId
を更新する必要があります。
警告
これを行う場合、各パス操作関数が一意の名前を持っていることを確認する必要があります。
異なるモジュール(Python ファイル)にある場合でもです。
OpenAPI から除外する¶
生成された OpenAPI スキーマ(および自動ドキュメントシステム)からパス操作を除外するには、include_in_schema
パラメータを使用し、False
に設定します。
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/", include_in_schema=False)
async def read_items():
return [{"item_id": "Foo"}]
ドックストリングからの高度な説明¶
OpenAPI 用にパス操作関数のドックストリングから使用される行数を制限できます。
\f
(エスケープされた「フォームフィード」文字)を追加すると、FastAPI はこの時点で OpenAPI に使用される出力を切り捨てます。
ドキュメントには表示されませんが、他のツール(Sphinx など)は残りの部分を使用できます。
from typing import Set, 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
tags: Set[str] = set()
@app.post("/items/", response_model=Item, summary="Create an item")
async def create_item(item: Item):
"""
Create an item with all the information:
- **name**: each item must have a name
- **description**: a long description
- **price**: required
- **tax**: if the item doesn't have tax, you can omit this
- **tags**: a set of unique tag strings for this item
\f
:param item: User input.
"""
return item
追加のレスポンス¶
パスオペレーションにresponse_model
とstatus_code
を宣言する方法はおそらくご存知でしょう。
これは、パスオペレーションのメインレスポンスに関するメタデータを定義します。
モデル、ステータスコードなどを含む追加のレスポンスを宣言することもできます。
ドキュメントにはこれに関する章全体があります。 OpenAPIにおける追加レスポンス で読むことができます。
OpenAPIの拡張¶
アプリケーションでパスオペレーションを宣言すると、FastAPIはOpenAPIスキーマに含めるために、そのパスオペレーションに関する関連メタデータを自動的に生成します。
「技術的な詳細」
OpenAPI仕様では、これはオペレーションオブジェクトと呼ばれています。
これはパスオペレーションに関するすべての情報を含んでおり、自動ドキュメントの生成に使用されます。
これには、tags
、parameters
、requestBody
、responses
などが含まれます。
このパスオペレーション固有のOpenAPIスキーマは通常、FastAPIによって自動的に生成されますが、拡張することもできます。
パラメータopenapi_extra
を使用して、パスオペレーションのOpenAPIスキーマを拡張できます。
OpenAPI拡張¶
このopenapi_extra
は、例えば、OpenAPI拡張を宣言するのに役立ちます。
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/", openapi_extra={"x-aperture-labs-portal": "blue"})
async def read_items():
return [{"item_id": "portal-gun"}]
自動APIドキュメントを開くと、拡張機能は特定のパスオペレーションの下部に表示されます。
また、結果のOpenAPI(APIの/openapi.json
)を見ると、拡張機能が特定のパスオペレーションの一部としても表示されます。
{
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"paths": {
"/items/": {
"get": {
"summary": "Read Items",
"operationId": "read_items_items__get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
}
},
"x-aperture-labs-portal": "blue"
}
}
}
}
カスタムOpenAPI *パスオペレーション*スキーマ¶
openapi_extra
の辞書は、パスオペレーション用に自動生成されたOpenAPIスキーマとディープマージされます。
そのため、自動生成されたスキーマに追加データを付加することができます。
たとえば、Pydanticを使用したFastAPIの自動機能を使用せずに、独自のコードでリクエストを読み取って検証することを決定できますが、それでもOpenAPIスキーマでリクエストを定義したい場合があります。
これはopenapi_extra
で実行できます。
from fastapi import FastAPI, Request
app = FastAPI()
def magic_data_reader(raw_body: bytes):
return {
"size": len(raw_body),
"content": {
"name": "Maaaagic",
"price": 42,
"description": "Just kiddin', no magic here. ✨",
},
}
@app.post(
"/items/",
openapi_extra={
"requestBody": {
"content": {
"application/json": {
"schema": {
"required": ["name", "price"],
"type": "object",
"properties": {
"name": {"type": "string"},
"price": {"type": "number"},
"description": {"type": "string"},
},
}
}
},
"required": True,
},
},
)
async def create_item(request: Request):
raw_body = await request.body()
data = magic_data_reader(raw_body)
return data
この例では、Pydanticモデルを宣言していません。実際、リクエストボディはJSONとして解析さえされておらず、bytes
として直接読み取られ、関数magic_data_reader()
が何らかの方法で解析を担当します。
それでも、リクエストボディの期待されるスキーマを宣言できます。
カスタムOpenAPIコンテンツタイプ¶
この同じ手法を使用して、Pydanticモデルを使用して、パスオペレーションのカスタムOpenAPIスキーマセクションに含まれるJSONスキーマを定義できます。
そして、リクエストのデータ型がJSONではない場合でも、これを行うことができます。
たとえば、このアプリケーションでは、PydanticモデルからJSONスキーマを抽出するFastAPIの統合機能も、JSONの自動検証も使用していません。実際、リクエストのコンテンツタイプをJSONではなくYAMLとして宣言しています。
from typing import List
import yaml
from fastapi import FastAPI, HTTPException, Request
from pydantic import BaseModel, ValidationError
app = FastAPI()
class Item(BaseModel):
name: str
tags: List[str]
@app.post(
"/items/",
openapi_extra={
"requestBody": {
"content": {"application/x-yaml": {"schema": Item.model_json_schema()}},
"required": True,
},
},
)
async def create_item(request: Request):
raw_body = await request.body()
try:
data = yaml.safe_load(raw_body)
except yaml.YAMLError:
raise HTTPException(status_code=422, detail="Invalid YAML")
try:
item = Item.model_validate(data)
except ValidationError as e:
raise HTTPException(status_code=422, detail=e.errors(include_url=False))
return item
from typing import List
import yaml
from fastapi import FastAPI, HTTPException, Request
from pydantic import BaseModel, ValidationError
app = FastAPI()
class Item(BaseModel):
name: str
tags: List[str]
@app.post(
"/items/",
openapi_extra={
"requestBody": {
"content": {"application/x-yaml": {"schema": Item.schema()}},
"required": True,
},
},
)
async def create_item(request: Request):
raw_body = await request.body()
try:
data = yaml.safe_load(raw_body)
except yaml.YAMLError:
raise HTTPException(status_code=422, detail="Invalid YAML")
try:
item = Item.parse_obj(data)
except ValidationError as e:
raise HTTPException(status_code=422, detail=e.errors())
return item
情報
Pydanticバージョン1では、モデルのJSONスキーマを取得するメソッドはItem.schema()
と呼ばれていましたが、Pydanticバージョン2では、メソッドはItem.model_json_schema()
と呼ばれています。
それでも、デフォルトの統合機能を使用していませんが、YAMLで受信したいデータのJSONスキーマを手動で生成するために、Pydanticモデルを使用しています。
次に、リクエストを直接使用し、ボディをbytes
として抽出します。これは、FastAPIがリクエストペイロードをJSONとして解析しようとさえしないことを意味します。
そして、コード内で、そのYAMLコンテンツを直接解析し、再び同じPydanticモデルを使用してYAMLコンテンツを検証します。
from typing import List
import yaml
from fastapi import FastAPI, HTTPException, Request
from pydantic import BaseModel, ValidationError
app = FastAPI()
class Item(BaseModel):
name: str
tags: List[str]
@app.post(
"/items/",
openapi_extra={
"requestBody": {
"content": {"application/x-yaml": {"schema": Item.model_json_schema()}},
"required": True,
},
},
)
async def create_item(request: Request):
raw_body = await request.body()
try:
data = yaml.safe_load(raw_body)
except yaml.YAMLError:
raise HTTPException(status_code=422, detail="Invalid YAML")
try:
item = Item.model_validate(data)
except ValidationError as e:
raise HTTPException(status_code=422, detail=e.errors(include_url=False))
return item
from typing import List
import yaml
from fastapi import FastAPI, HTTPException, Request
from pydantic import BaseModel, ValidationError
app = FastAPI()
class Item(BaseModel):
name: str
tags: List[str]
@app.post(
"/items/",
openapi_extra={
"requestBody": {
"content": {"application/x-yaml": {"schema": Item.schema()}},
"required": True,
},
},
)
async def create_item(request: Request):
raw_body = await request.body()
try:
data = yaml.safe_load(raw_body)
except yaml.YAMLError:
raise HTTPException(status_code=422, detail="Invalid YAML")
try:
item = Item.parse_obj(data)
except ValidationError as e:
raise HTTPException(status_code=422, detail=e.errors())
return item
情報
Pydanticバージョン1では、オブジェクトを解析して検証するメソッドはItem.parse_obj()
でしたが、Pydanticバージョン2では、メソッドはItem.model_validate()
と呼ばれています。
ヒント
ここでは、同じPydanticモデルを再利用しています.
しかし、同じように、他の方法で検証することもできました。