スキーマ

スキーマ

機械可読な[スキーマ]は、APIを介して利用可能なリソース、それらのURL、それらがどのように表現されるか、そしてそれらがサポートする操作を記述します。

— Heroku、HerokuプラットフォームAPIのJSONスキーマ


非推奨に関するお知らせ

REST frameworkの組み込みOpenAPIスキーマ生成サポートは、代わりにこの機能を提供できるサードパーティパッケージを支持して**非推奨**となりました。組み込みサポートは、別のパッケージに移行され、その後、次のリリースで廃止される予定です。

完全な代替手段として、drf-spectacularパッケージをお勧めします。REST framework APIからOpenAPI 3スキーマを生成するための広範なサポートがあり、自動およびカスタマイズ可能なオプションの両方が利用可能です。詳細については、APIのドキュメント化を参照してください。


APIスキーマは、参照ドキュメントの生成や、APIと対話できる動的クライアントライブラリの駆動など、さまざまなユースケースを可能にする便利なツールです。

Django REST Frameworkは、OpenAPIスキーマの自動生成をサポートしています。

概要

スキーマ生成には、いくつかの可動部分があります。概要を把握しておく価値があります

  • SchemaGeneratorは、設定されたURLパターンをウォークし、APIViewサブクラスを見つけ、それらのスキーマ表現を照会し、最終的なスキーマオブジェクトをコンパイルする役割を担うトップレベルクラスです。
  • AutoSchemaは、ビューごとのスキーマイントロスペクションに必要なすべての詳細をカプセル化します。各ビューに`schema`属性を介してアタッチされます。スキーマをカスタマイズするには、`AutoSchema`をサブクラス化します。
  • `generateschema`管理コマンドを使用すると、静的スキーマをオフラインで生成できます。
  • または、`SchemaView`をルーティングして、スキーマを動的に生成および提供することもできます。
  • `settings.DEFAULT_SCHEMA_CLASS`を使用すると、プロジェクトのデフォルトとして機能する`AutoSchema`サブクラスを指定できます。

以下のセクションで詳しく説明します。

OpenAPIスキーマの生成

依存関係のインストール

pip install pyyaml uritemplate inflection
  • `pyyaml`は、スキーマをYAMLベースのOpenAPI形式に生成するために使用されます。
  • `uritemplate`は、パス内のパラメータを取得するために内部的に使用されます。
  • `inflection`は、リストエンドポイントで操作をより適切に複数形にするために使用されます。

`generateschema`管理コマンドを使用した静的スキーマの生成

スキーマが静的な場合は、`generateschema`管理コマンドを使用できます

./manage.py generateschema --file openapi-schema.yml

この方法でスキーマを生成したら、スキーマジェネレータによって自動的に推測できない追加情報で注釈を付けることができます。

APIスキーマをバージョン管理にチェックインし、新しいリリースごとに更新するか、サイトの静的メディアからAPIスキーマを提供することができます。

`SchemaView`を使用した動的スキーマの生成

外部キーの選択肢がデータベースの値に依存するなどの理由で動的スキーマが必要な場合は、オンデマンドでスキーマを生成して提供する`SchemaView`をルーティングできます。

`SchemaView`をルーティングするには、`get_schema_view()`ヘルパーを使用します。

`urls.py`で

from rest_framework.schemas import get_schema_view

urlpatterns = [
    # ...
    # Use the `get_schema_view()` helper to add a `SchemaView` to project URLs.
    #   * `title` and `description` parameters are passed to `SchemaGenerator`.
    #   * Provide view name for use with `reverse()`.
    path(
        "openapi",
        get_schema_view(
            title="Your Project", description="API for all things …", version="1.0.0"
        ),
        name="openapi-schema",
    ),
    # ...
]

get_schema_view()

`get_schema_view()`ヘルパーは、次のキーワード引数を取ります

  • `title`:スキーマ定義の記述的なタイトルを提供するために使用できます。
  • `description`:より長い説明テキスト。
  • `version`:APIのバージョン。
  • `url`:スキーマの正規ベースURLを渡すために使用できます。

    schema_view = get_schema_view(
        title='Server Monitoring API',
        url='https://www.example.org/api/'
    )
  • `urlconf`:APIスキーマを生成するURLconfへのインポートパスを表す文字列。これは、Djangoの`ROOT_URLCONF`設定の値にデフォルト設定されます。

    schema_view = get_schema_view(
        title='Server Monitoring API',
        url='https://www.example.org/api/',
        urlconf='myproject.urls'
    )
  • `patterns`:スキーマイントロスペクションを制限するURLパターンのリスト。 `myproject.api` URLのみをスキーマに公開する場合

    schema_url_patterns = [
        path('api/', include('myproject.api.urls')),
    ]
    
    schema_view = get_schema_view(
        title='Server Monitoring API',
        url='https://www.example.org/api/',
        patterns=schema_url_patterns,
    )
    • `public`:スキーマがビューのパーミッションをバイパスする必要があるかどうかを指定するために使用できます。デフォルトはFalseです
  • `generator_class`: `SchemaView`に渡される`SchemaGenerator`サブクラスを指定するために使用できます。

  • `authentication_classes`:スキーマエンドポイントに適用される認証クラスのリストを指定するために使用できます。デフォルトは`settings.DEFAULT_AUTHENTICATION_CLASSES`です
  • `permission_classes`:スキーマエンドポイントに適用されるパーミッションクラスのリストを指定するために使用できます。デフォルトは`settings.DEFAULT_PERMISSION_CLASSES`です。
  • `renderer_classes`:APIルートエンドポイントのレンダリングに使用できるレンダラークラスのセットを渡すために使用できます。

SchemaGenerator

スキーマレベルのカスタマイズ

from rest_framework.schemas.openapi import SchemaGenerator

`SchemaGenerator`は、ルーティングされたURLパターンのリストをウォークし、各ビューのスキーマをリクエストし、結果のOpenAPIスキーマを照合するクラスです。

通常、`SchemaGenerator`を自分でインスタンス化する必要はありませんが、次のようにインスタンス化できます

generator = SchemaGenerator(title='Stock Prices API')

引数

  • `title` **必須**:APIの名前。
  • `description`:より長い説明テキスト。
  • `version`:APIのバージョン。デフォルトは`0.1.0`です。
  • `url`:APIスキーマのルートURL。スキーマがパスプレフィックスの下に含まれていない限り、このオプションは必須ではありません。
  • `patterns`:スキーマの生成時に検査するURLのリスト。デフォルトはプロジェクトのURLconfです。
  • `urlconf`:スキーマの生成時に使用するURLconfモジュール名。デフォルトは`settings.ROOT_URLCONF`です。

トップレベルスキーマをカスタマイズするには、`rest_framework.schemas.openapi.SchemaGenerator`をサブクラス化し、`generateschema`コマンドまたは`get_schema_view()`ヘルパー関数に引数としてサブクラスを提供します。

get_schema(self, request=None, public=False)

OpenAPIスキーマを表す辞書を返します

generator = SchemaGenerator(title='Stock Prices API')
schema = generator.get_schema()

`request`引数はオプションであり、ユーザーごとのパーミッションを結果のスキーマ生成に適用する場合に使用できます。

これは、生成された辞書をカスタマイズする場合にオーバーライドするのに適したポイントです。たとえば、トップレベルの`info`オブジェクトにサービス条件を追加することができます

class TOSSchemaGenerator(SchemaGenerator):
    def get_schema(self, *args, **kwargs):
        schema = super().get_schema(*args, **kwargs)
        schema["info"]["termsOfService"] = "https://example.com/tos.html"
        return schema

AutoSchema

ビューごとのカスタマイズ

from rest_framework.schemas.openapi import AutoSchema

デフォルトでは、ビューのイントロスペクションは、`APIView`の`schema`属性を介してアクセスできる`AutoSchema`インスタンスによって実行されます。

auto_schema = some_view.schema

`AutoSchema`は、各ビュー、リクエストメソッド、およびパスに必要なOpenAPI要素を提供します

  • OpenAPIコンポーネントのリスト。DRFの用語では、これらはリクエストとレスポンスの本文を記述するシリアライザーのマッピングです。
  • ページネーション、フィルタリングなどのパスとクエリパラメータを含む、エンドポイントを記述する適切なOpenAPI操作オブジェクト
components = auto_schema.get_components(...)
operation = auto_schema.get_operation(...)

スキーマのコンパイルでは、`SchemaGenerator`は各ビュー、許可されたメソッド、およびパスに対して`get_components()`と`get_operation()`を呼び出します。


**注**:コンポーネントの自動イントロスペクション、および多くの操作パラメータは、`GenericAPIView`の関連する属性とメソッドに依存しています:`get_serializer()`、`pagination_class`、`filter_backends`など。基本的な`APIView`サブクラスの場合、デフォルトのイントロスペクションはこの理由でURLのkwargパスパラメータに essentially 制限されます。


`AutoSchema`は、スキーマ生成に必要なビューイントロスペクションをカプセル化します。このため、すべてのスキーマ生成ロジックは、すでに広範なビュー、シリアライザー、およびフィールドAPIに分散されるのではなく、1か所に保持されます。

このパターンに従って、スキーマ生成をカスタマイズするときに、スキーマロジックが独自のビュー、シリアライザー、またはフィールドにリークしないようにしてください。次のようなことをしたくなるかもしれません

class CustomSchema(AutoSchema):
    """
    AutoSchema subclass using schema_extra_info on the view.
    """

    ...


class CustomView(APIView):
    schema = CustomSchema()
    schema_extra_info = ...  # some extra info

ここで、`AutoSchema`サブクラスは、ビューで`schema_extra_info`を探します。これは*OK*です(実際には害はありません)が、スキーマロジックが 여러 곳に分散することになります。

代わりに、`extra_info`がビューにリークしないように`AutoSchema`をサブクラス化してみてください

class BaseSchema(AutoSchema):
    """
    AutoSchema subclass that knows how to use extra_info.
    """

    ...


class CustomSchema(BaseSchema):
    extra_info = ...  # some extra info


class CustomView(APIView):
    schema = CustomSchema()

このスタイルは少し冗長ですが、スキーマ関連コードのカプセル化を維持します。*専門用語*では、より*凝集性*があります。APIコードの残りの部分をよりきれいに保ちます。

オプションが多くのビュークラスに適用される場合は、ビューごとに特定のサブクラスを作成するのではなく、オプションをベース`AutoSchema`サブクラスに`__init__()` kwargとして指定できるようにする方が便利な場合があります

class CustomSchema(BaseSchema):
    def __init__(self, **kwargs):
        # store extra_info for later
        self.extra_info = kwargs.pop("extra_info")
        super().__init__(**kwargs)


class CustomView(APIView):
    schema = CustomSchema(extra_info=...)  # some extra info

これにより、一般的に使用されるオプションのためにビューごとにカスタムサブクラスを作成する必要がなくなります。

すべての`AutoSchema`メソッドが関連する`__init__()` kwargsを公開するわけではありませんが、より一般的に必要なオプションのメソッドは公開します。

`AutoSchema`メソッド

get_components()

リクエストとレスポンスの本文を記述するOpenAPIコンポーネントを生成し、シリアライザーからプロパティを派生させます。

コンポーネント名と生成された表現をマッピングする辞書を返します。デフォルトでは、これは1つのペアのみですが、ビューが複数のシリアライザーを使用する場合は、`get_components()`をオーバーライドして複数のペアを返すことができます。

get_component_name()

シリアライザーからコンポーネントの名前を計算します。

APIに重複したコンポーネント名がある場合は、警告が表示されることがあります。その場合は、`get_component_name()`をオーバーライドするか、`component_name` `__init__()` kwarg(下記参照)を渡して異なる名前を付けることができます。

get_reference()

シリアライザーコンポーネントへの参照を返します。 `get_schema()`をオーバーライドする場合に役立ちます。

map_serializer()

シリアライザーをOpenAPI表現にマップします。

ほとんどのシリアライザーは標準のOpenAPI `object`タイプに準拠する必要がありますが、これをカスタマイズするために`map_serializer()`をオーバーライドするか、他のシリアライザーレベルのフィールドをオーバーライドすることができます。

map_field()

個々のシリアライザフィールドをスキーマ表現にマッピングします。基本実装は、Django REST Framework が提供するデフォルトフィールドを処理します。

スキーマが不明な `SerializerMethodField` インスタンス、またはカスタムフィールドサブクラスの場合、正しいスキーマを生成するために `map_field()` をオーバーライドする必要があります。

class CustomSchema(AutoSchema):
    """Extension of ``AutoSchema`` to add support for custom field schemas."""

    def map_field(self, field):
        # Handle SerializerMethodFields or custom fields here...
        # ...
        return super().map_field(field)

サードパーティパッケージの作成者は、`AutoSchema` サブクラスと `map_field()` をオーバーライドする mixin を提供することを目指すべきです。これにより、ユーザーはカスタムフィールドのスキーマを簡単に生成できます。

get_tags()

OpenAPI は、タグによって操作をグループ化します。デフォルトでは、タグはルーティングされた URL の最初のパスセグメントから取得されます。たとえば、`/users/{id}/` のような URL は、`users` タグを生成します。

タグを手動で指定するために `__init__()` にキーワード引数を渡すか(下記参照)、カスタムロジックを提供するために `get_tags()` をオーバーライドできます。

get_operation()

エンドポイントを記述する OpenAPI 操作オブジェクト を返します。これには、ページネーション、フィルタリングなどのパスとクエリパラメータが含まれます。

`get_components()` とともに、これはビューイントロスペクションへの主要なエントリポイントです。

get_operation_id()

各操作には、一意の operationId が必要です。デフォルトでは、`operationId` はモデル名、シリアライザ名、またはビュー名から推測されます。 `operationId` は "listItems"、"retrieveItem"、"updateItem" などのようになります。 `operationId` は慣例によりキャメルケースです。

get_operation_id_base()

同じモデル名を持つ複数のビューがある場合、重複した operationId が表示されることがあります。

これを回避するために、`get_operation_id_base()` をオーバーライドして、ID の名前部分に異なるベースを提供できます。

get_serializer()

ビューが `get_serializer()` を実装している場合、その結果を返します。

get_request_serializer()

デフォルトでは `get_serializer()` を返しますが、リクエストオブジェクトとレスポンスオブジェクトを区別するためにオーバーライドできます。

get_response_serializer()

デフォルトでは `get_serializer()` を返しますが、リクエストオブジェクトとレスポンスオブジェクトを区別するためにオーバーライドできます。

`AutoSchema.__init__()` キーワード引数

`AutoSchema` は、デフォルトで生成された値が適切でない場合に、一般的なカスタマイズに使用できる多数の `__init__()` キーワード引数を備えています。

使用可能なキーワード引数は次のとおりです。

  • `tags`:タグのリストを指定します。
  • `component_name`:コンポーネント名を指定します。
  • `operation_id_base`:操作 ID のリソース名部分を指定します。

ビューで `AutoSchema` インスタンスを宣言するときに、キーワード引数を渡します。

class PetDetailView(generics.RetrieveUpdateDestroyAPIView):
    schema = AutoSchema(
        tags=['Pets'],
        component_name='Pet',
        operation_id_base='Pet',
    )
    ...

`Pet` モデルと `PetSerializer` シリアライザを想定すると、この例のキーワード引数は必要ないでしょう。ただし、同じモデルをターゲットとする複数のビューがある場合、または同じ名前のシリアライザを持つ複数のビューがある場合は、キーワード引数を渡す必要があることがよくあります。

ビューに頻繁に必要な関連するカスタマイズがある場合は、プロジェクトのベース `AutoSchema` サブクラスを作成し、追加の `__init__()` キーワード引数を受け取って、ビューごとに `AutoSchema` のサブクラス化を回避できます。