Django REST frameworkの初期セットアップ
Tags: django, python, tutorial
Published: 2022-01-20 10:36:45

Django REST framework（以下DRF）でバックエンドを構築する際の初期設定をメモ代わりに書いておきます。
（書かないと無限に忘れるので・・・）

## プロジェクトの作成
プロジェクト名は`tutorial`としておきます。
ディレクトリを作成して仮想環境(venv)が導入されている前提です。

まずは`requirements.txt`に必要最低限のライブラリのみ書いておきます。
よく使うライブラリもこのタイミングで入れていいと思います。

```txt
django
djangorestframework
```

必要パッケージのインストール

```ps
pip install -r requirements.txt
```

Djangoプロジェクトの作成をコマンドで行います。
この辺は完全に好みだと思うのですが、`プロジェクト名/プロジェクト名/settings.py`みたいにプロジェクト名が二重にネストするのがちょっと気持ち悪いので、この設定ファイルなどがあるディレクトリ名を`config`にするために、下記のようにDjangoプロジェクトを作成します。
仮想環境を入れたプロジェクトファイル下で下記のコマンドを実行します。

```ps
django-admin startproject config .
```

プロジェクト全体のディレクトリ構成は下記の様になります。

```bash
tutorial
├─ venv
├─ requirements.txt
├─ manage.py
└─ config
   ├─ __init__.py
   ├─ asgi.py
   ├─ settings.py
   ├─ urls.py
   └─ wsgi.py
```

機能的には`tutorial/tutorial`になってもなんら問題無いのでこの辺は完全に好みです。むしろこっちの方が面倒。
動作チェックはDRFとして動いて欲しいので、適当なモデルを作成してから行います。

## アプリケーションの追加
試しにTodoを管理するアプリケーションを想定して構築してみます。
DRFはAPIエンドポイントとして運用される想定なので、一応バージョニングを意識して

- APIエンドポイント用のアプリケーション（ここでは`apiv1`）
- モデル用のアプリケーション（ここでは`todos`）

を分離しておきます。
`manage.py`でアプリケーションを作成しておきます。

```ps
python manage.py startapp apiv1
python manage.py startapp todos
```

ここで初期設定もかねて`config/settings.py`を編集します。

```
...

from pathlib import Path
from django.conf.locale.ja import formats as ja_formats

...

INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "rest_framework",
    "apiv1.apps.Apiv1Config",
    "todos.apps.TodosConfig",
]

...

# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/

LANGUAGE_CODE = "ja"

TIME_ZONE = "Asia/Tokyo"

USE_I18N = True

USE_L10N = True

USE_TZ = True

ja_formats.DATE_FORMAT = "Y/m/d"
ja_formats.DATE_INPUT_FORMATS = ("%Y/%m/%d",)
ja_formats.DATETIME_FORMAT = "Y/m/d H:i:s"
ja_formats.DATETIME_INPUT_FORMATS = ("%Y-%m-%d %H:%M:%S",)

...
```

`INSTALLED_APPS`の追記は、Django 3.2からAutomatic AppConfig discoveryで不要になったと聞いたのですが、自分の環境では何故か管理画面にモデルが表示されないので追記しています。

## モデルの作成
### models.pyの編集
何はともあれモデルがないと始まりません。
Todoモデル`todos/models.py`を作成していきます。

```python
import uuid
from django.db import models


class Todo(models.Model):
    class Meta:
        db_table = "todo"

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    title = models.CharField(max_length=200)
    checked = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.title

```

idをUUIDにしていますが、ここは好みです。（とはいえ、UUIDの方が推奨されそうですが・・・）

### admin.pyの編集
モデルを作ったら管理画面で編集できる様にしておきたいです。
`todos/admin.py`を編集します。

```python
from django.contrib import admin

from .models import Todo


class TodoAdmin(admin.ModelAdmin):
    fields = ["title", "checked"]
    list_display = ("title", "checked", "id", "created_at", "updated_at")


admin.site.register(Todo, TodoAdmin)

```


一応管理画面を確認しておきたいので念のためDBを更新してから`createsuperuser`で管理ユーザーを作成しておきます。

```ps
python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser
```

ここまでやれば管理画面からモデルを操作できるので、動作に不安のある場合はこの辺で新規作成などしてみても良いと思うます。

## シリアライザの作成
DRF特有の存在であるシリアライザを作成します。
これはAPIの機能なので`apiv1/serializers.py`に作成します。

```python
from rest_framework import serializers

from todos.models import Todo


class TodoSerializer(serializers.ModelSerializer):
    class Meta:
        model = Todo
        fields = [
            "id",
            "title",
            "checked",
        ]

```

## ビューの作成
API用のビュー`apiv1/views.py`を編集します。

```python
from rest_framework import viewsets

from todos.models import Todo
from .serializers import TodoSerializer


class TodoViewSet(viewsets.ModelViewSet):
    queryset = Todo.objects.all()
    serializer_class = TodoSerializer

```

## ルーティングの設定
大体の機能は実装できたので、ルーティングの設定をしておきます。
まずは`apiv1/urls.py`を作成します。

```python
from django.urls import path, include
from rest_framework import routers

from . import views

router = routers.DefaultRouter()
router.register("todos", views.TodoViewSet, basename="todos")

app_name = "apiv1"
urlpatterns = [path("", include(router.urls))]

```


> routerに`todos`を登録する際に`basename="todos"`と指定しているのは、`django.urls`の`reverse`関数で逆引きする際に`apiv1:todos-list`としたい為です。コレを指定していない場合はビューの`queryset`に登録したモデルの名称が採用されて`apiv1:todo-list`と参照されてしまう様です。


今回はビューをModelViewSetで実装したのでルーティングが楽です。
あとは`config/urls.py`を編集します。

```python
from django.contrib import admin
from django.urls import path, include

urlpatterns = [path("admin/", admin.site.urls), path("api/v1/", include("apiv1.urls"))]

```

ここまでやれば、APIとしては実装完了です。

## 動作チェック
DRFはデバッグモードではブラウザ上で色々確認出来ます。
試しにアクセスしてみると、何もないリストが返却されているのが確認出来ると思います。

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tj21gjvl0sb0cwhaprfz.png)

POST用のフォームもあるので、そちらから追加して見るもヨシ、cURLなどで追加して見るもヨシです。

## おわり
上記だけでCRUD操作ができるAPIが構築でき、管理画面も使いやすいしカスタマイズしやすいのがDRFの良さだと思います。
本来であれば認証まわりも実装すると思いますが、今回は簡略化のため省きました。
実際に実装する場合はDRFの設定（settings.py）とビューでの設定などが必要になると思います。

## 参考
- [現場で使える Django REST Framework の教科書](https://amzn.to/3FgLziJ)
- [Django REST framework](https://www.django-rest-framework.org/)
- [Django ドキュメント](https://docs.djangoproject.com/ja/4.0/)