DjangoRestFrameworkのコードからSwaggerドキュメントを生成しAPI設計を共有


弊社開発環境

弊社のエンジニアチームでは以下の開発環境が最近の主流です。

サーバーサイド: Django + DjangoRestFramework(API)
WEBクライアント: Vue.js
アプリクライアント: Flutter( + Swift)( + Kotlin)

サーバーサイドとクライアントサイドで違う人が開発する場合は多く、
そのようなときにAPI設計を共有するのって難しいですよね。
そのときにSwaggerを使ったAPI設計の共有ドキュメントを生成しています。

イメージ図(Swaggerを書くと1つのAPIでこれだけの情報が取り出せます)

どうやって書いているのか

コードからドキュメントを自動生成しています。

class AbcApiView(ApiView):
    @swagger_auto_schema(
        request_body=AbcRequestSerializer,
        responses={200: openapi.Response('OK', AbcResponseSerializer)})
    def post(self, request, *args, **kwargs):
        """
        期間限定キャンペーンで無料になっている商品を一覧表示
        """
        pass

DRFでSerializerを作っている人は、request_bodyとresponsesにSerializerの型を渡すだけ😍
これだけでドキュメントを生成してくれます。
ドキュメントを追加するのに3行のコードと1行の説明文(docstring)だけで済みます!!

ついでにSerializerも貼っておきます。

class AbcRequestSerializer(serializers.Serializer):
    page = serializers.IntegerField(default=1)
    genre = serializers.CharField(default='', allow_blank=True, allow_null=True)

class AbcApiResponseSerializer(serializers.Serializer):
    items = serializers.ListField(child=ZyxSerializer(), default=[])
    page = serializers.IntegerField()
    has_next = serializers.BooleanField()
    has_prev = serializers.BooleanField()
    max_page = serializers.IntegerField()

シリアライザーにフィールドを追加したいとき、ドキュメントをメンテしなくて済むのでラクチンですね。

yamlでも書ける

yamlで書いてみると以下のようになります。

paths:
  /api/campaign/v1/:
    post:
      summary: 期間限定キャンペーンで無料になっている商品を一覧表示
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                page:
                  type: integer
                genre:
                  type: string
                  default: ""
              example:   # Sample object
                page: 2
                name: "MANGA"
      responses:
        '200':
          description: OK

長い!!!! ( responseを書くのを諦めた

設計段階の場合はこれでいいのですが、たくさんのエンドポイントを作っているとファイルが1000行くらいすぐに増えます。またメンテもしなくなるので、古いドキュメントが完成です。

docstringの部分には、もうちょっと複雑なことも書ける

docstringの部分には、htmlやコードも書けます。

class AbcApiView(ApiView):
    def post(self, request, *args, **kwargs):
    """
    まとめ買いのリスト表示を行います。

    :param tcode: 基準となるコードを入力します。

    :return 200 ok_response

    ```
    [{
        title_code: 'tcode1', // タイトルコードが一意でない場合がある。シリーズもの。
        serial_number: 1 // tcodeとserial_numberの2つで一意
        price: 0, // 購入するための金額
        is_purchased: false, // 購入済みの場合はグレーアウト
        is_selected: false, // 購入対象をユーザーが選択
        regular_price: 100, // 通常価格
        campaign_text: 'campaign' // 無料・半額キャンペーン情報
        thumbnail_image_path: '/favicon/favicon256.png' // サムネイル
    },]
    ```
    priceとregular_priceは違うものになります。<br>
    regular_priceは通常価格。<br>
    500円のものを途中まで購入している場合は、priceは300
    """

インストール & 設定

https://github.com/axnsan12/drf-yasg
に従ってインストールすればすぐに使えました😁

一点だけ変更したところを記載します。

schema_view = get_schema_view(
    ....
    permission_classes=(permissions.AllowAny,) if settings.DEBUG else (permissions.IsAdminUser,),
)

permission_classesは閲覧権限です。
ローカルではログインしていなくても閲覧できますが、ローカル以外では管理者権限でないと閲覧できないようにしました。

以上です。
明日は弊社CTOの@ytyng先生のDjangoAdmin教室です!