パス操作の高度な設定¶
OpenAPI operationId¶
警告
OpenAPIの「エキスパート」でない場合は、おそらくこれが必要ないでしょう。
operation_id
パラメーターを使って、パス操作で使用するOpenAPIのoperationId
を設定できます。
各操作で一意であることを確認する必要があります。
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"}]
Docstringからの高度な説明¶
OpenAPI用に、パス操作関数のdocstringから使用される行数を制限できます。
\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 Extra¶
アプリケーションでパス操作を宣言すると、FastAPIは、そのパス操作に関連するメタデータを自動的に生成し、OpenAPIスキーマに含めます。
技術的な詳細
OpenAPI仕様では、Operation Objectと呼ばれます。
パス操作に関するすべての情報が含まれており、自動ドキュメントの生成に使用されます。
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モデルでJSONスキーマを定義し、それをパス操作のカスタムOpenAPIスキーマセクションに含めることができます。
そして、リクエストのデータ型がJSONでなくてもこれを行うことができます。
たとえば、このアプリケーションでは、FastAPIの統合機能を使用してPydanticモデルからJSONスキーマを抽出したり、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モデルを再利用しています。
しかし、同じように、他の方法で検証することもできたはずです。