Django REST FrameworkでのAPI実装からSwagger UIでスキーマを表示してみた

#### はじめに
先日Django REST Framework（DRF）を利用してAPIを作成しました。
APIのドキュメントをつくることも考慮すると、Swaggerのドキュメント生成を基本機能としてサポートしているFastAPIに傾きかけましたが、やはりDjangoの強力な管理機能は業務アプリに欠かせません。
DRFで作成したAPIのドキュメントを簡単に作成できないかと調べていたところ、少しの工夫でFastAPIと同様にAPIドキュメントを自動作成することができることを知りました。
今回は、DRFで実装したAPIからAPIドキュメントの作成を自動化する手順をここに残します。

先人たちの知恵をお借りするなどして解決できたことを、この場をお借りして感謝するとともに、大変恐縮ですが**自分のメモ**として、こちらへまとめておきます。

:::note info
今回使用する環境等のバージョン

Windows 11 Pro 22H2
Python 3.12.0
Django 4.2.6
Django REST Framework 3.14.0
SQLite 3.43.1
:::

### 1.Swaggerとは
SwaggerはRESTful APIを構築するためのオープンソースのフレームワークです。
「Open API Initiative」という団体が推進している、RESTful APIのインターフェイスの記述をするための標準フォーマットがSwaggerです。Swaggerには多くの便利なツールも提供されていることもあり、メリットも大きいと感じます。
Swagger Specを書くことで自動的にドキュメントを生成してくれるだけではなく、ドキュメントから実際のリクエストを投げられる優れものです。

#### Swagger関連の用語

| 用語 | 説明 | 
| :--- | :--- | 
| Swagger Specification	| Swaggerを扱う上で中心となる概念で、Swaggerの仕様に準じた、RESTful APIインターフェイスを記述するためのフォーマット（YAML/JSON）。Swagger Editorなどで編集するときには、YAMLを用いる方が扱いやすいが、外部のライブラリやSwagger UIと連携する場合にはJSON形式で使用。 | 
| OpenAPI Specification | Swaggerをベースに作られたRESTful APIインターフェイスを記述するためのフォーマット（JSON）。 | 
| Swagger Editer | ブラウザ上で動くSwagger Specificationの設計書を記載するためのエディタ。リアルタイムで構文チェック、可視化。 | 
| Swagger UI | Swagger Specificationで記載された設計からドキュメントをHTML形式で自動生成するツール。 | 
| Swagger Codegen | Swagger Specificationで記載された設計からクライアントコード生成するコマンドラインツール。APIのスタブを自動生成する。 | 

https://swagger.io/

### 2.APIドキュメントを作成する
#### 2-1) OpenAPI Rendererに必要なライブラリをインストール
Djangoプロジェクトが稼働しているpython仮想環境でコマンドを実行します。
```terminal
(venv) pip install pyyaml
(venv) pip install uritemplate
```

#### 2-2) djangoプロジェクトディレクトリのurls.pyを編集
プロジェクトレベルのルーティングにスキーマへのルーティングを追記します。

```diff_python:project/urls.py
from django.contrib import admin
from django.urls import include, path
+ from rest_framework.schemas import get_schema_view
+ from django.views.generic import TemplateView

urlpatterns = [
    path('admin/', admin.site.urls),

    ...

+     path('schema/', get_schema_view(          # スキーマ表示の追加
+         title="任意",
+         description="任意"
+     ), name='openapi-schema'),
+     path('docs/', TemplateView.as_view(       # ドキュメント表示の追加
+         template_name='swagger-ui.html',
+         extra_context={'schema_url':'openapi-schema'}
+     ), name='swagger-ui'),
]
```

#### 2-3) テンプレートファイルを作成
ドキュメントを表示するためのテンプレートファイルが必要になるため、テンプレートディレクトリをproject/settings.pyに設定します。
```diff_python:project/settings.py
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
-         'DIRS': [], # リスト内に追記
+         'DIRS': ['templates'],       # リスト内に追記
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]
```

上記設定に対応するようにdjangoプロジェクトのルートディレクトリ直下に、templates/ディレクトリを作成します。
スキーマのドキュメントを表示するためのテンプレートはいくつか用意されているようですが、今回はswagger-ui.htmlを利用するため、templates/swagger-ui.htmlファイルをつくり、以下のコードを追記します。
```diff_html:templates/swagger-ui.html
<!DOCTYPE html>
<html>
  <head>
    <title>API References</title>
    <meta charset="utf-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" type="text/css" href="//unpkg.com/swagger-ui-dist@3/swagger-ui.css" />
  </head>
  <body>
    <div id="swagger-ui"></div>
    <script src="//unpkg.com/swagger-ui-dist@3/swagger-ui-bundle.js"></script>
    <script>
    const ui = SwaggerUIBundle({
        url: "{% url schema_url %}",
        dom_id: '#swagger-ui',
        presets: [
          SwaggerUIBundle.presets.apis,
          SwaggerUIBundle.SwaggerUIStandalonePreset
        ],
        layout: "BaseLayout"
      })
    </script>
  </body>
</html>
```

先日作成したDRFのAPIを例とすると、ディレクトリ構成は以下のようになります。

https://qiita.com/_whitecat_22/items/ffa3a556c9e01f8774aa

```config:ディレクトリ構成
root （プロジェクトルート）
├ djangoApiApp （プロジェクト）
  ├ __pycache__
  ├ __init__.py
  ├ asgi.py
  ├ settings.py
  └ wsgi.py
├ products （アプリケーション）
  ├ __pycache__
  ├ migtarions
  ├ __init__.py
  ├ admin.py
  ├ apps.py
  ├ models.py
  ├ serializers.py
  ├ tests.py
  ├ urls.py
  └ views.py
├ templates            # 追加
  └ swagger-ui.html    # 追加
└ venv
  ├ Include
  ├ Lib
  ├ Scripts
  └ pyvenv.cfg
.env
db.sqlite3
manage.py
```

#### 2-4) APIドキュメントを確認
これにより`http://localhost:8000/schema/`や`http://localhost:8000/docs/`にアクセスするとそれぞれの表示形式でAPIスキーマを確認できます。（localhostを127.0.0.1としても可）

こちらはDRFの標準機能で確認できるスキーマです。（`http://localhost:8000/schema/`）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/902793/17e6549a-9576-eb90-5458-91161de34e86.png)

そしてこちらがswagger-ui.htmlで装飾表示されたスキーマです。（`http://localhost:8000/docs/`）
とても見やすいですよね！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/902793/a8e003a2-61e0-3d2c-2426-a34551d7dcc4.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/902793/84f3417e-a90b-53ee-0663-d66a3ec55b38.png)

---
今回のソースコードもGitHubにコミット済みです。

https://github.com/whitecat-22/drf_crud_and_api_doc.git

---
#### 参考

OpenAPI Specification

https://spec.openapis.org/oas/latest.html

Swagger

https://swagger.io/

https://qiita.com/KNR109/items/7e094dba6bcf37ed73cf

ReDocというものもあるらしい...

https://github.com/Redocly/redoc

https://redocly.github.io/redoc/

---
#### (編集後記)
今回はDjano REST Frameworkを用いたAPIの実装コードから、Swagger Specificationを生成しSwagger UIで表示することができました。
本来、設計→実装の順で進めるべきではありますが、コードを改修をしてもドキュメントの改修が為されていないシステムを過去に嫌というほど経験してきました。
実装からAPIドキュメントを作成するというリバースエンジニアリングにはなりますが、常に最新の実装を反映できる点はとても良いと思いました。
DRFでもFastAPIと同様のことができ、自分自身はとても満足です。ぜひみなさまの参考になれば幸いです。