LoginSignup
19

More than 3 years have passed since last update.

posted at

updated at

Organization

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

弊社開発環境

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

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

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

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

スクリーンショット_2019-12-06_21_29_21.png

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

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

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
    """

スクリーンショット_2019-12-06_21_40_44.png

インストール & 設定

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教室です!

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
19