コンテンツにスキップ

パス操作の高度な設定

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_modelstatus_codeを宣言する方法はおそらくご存知でしょう。

これは、パスオペレーションのメインレスポンスに関するメタデータを定義します。

モデル、ステータスコードなどを含む追加のレスポンスを宣言することもできます。

ドキュメントにはこれに関する章全体があります。 OpenAPIにおける追加レスポンス で読むことができます。

OpenAPIの拡張

アプリケーションでパスオペレーションを宣言すると、FastAPIはOpenAPIスキーマに含めるために、そのパスオペレーションに関する関連メタデータを自動的に生成します。

「技術的な詳細」

OpenAPI仕様では、これはオペレーションオブジェクトと呼ばれています。

これはパスオペレーションに関するすべての情報を含んでおり、自動ドキュメントの生成に使用されます。

これには、tagsparametersrequestBodyresponsesなどが含まれます。

このパスオペレーション固有のOpenAPIスキーマは通常、FastAPIによって自動的に生成されますが、拡張することもできます。

ヒント

これは低レベルの拡張ポイントです。

追加のレスポンスを宣言するだけの場合、より便利な方法はOpenAPIにおける追加レスポンスを使用することです。

パラメータ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モデルを再利用しています.

しかし、同じように、他の方法で検証することもできました。