素人基盤エンジニアがDockerでDjango REST Frameworkとreactを触るシリーズ①：Djangoの導入

# 前提条件
- EC2ServerのOS: AmazonLinux2
- docker導入済み
- docker-compose導入済み

# はじめに
今回はDjango REST Frameworkとreactを使って、簡単なToDoアプリを実装する。
実装基盤として、AWS_EC2上でbackend用のdjangoとdatabase(postgreSQL)をdockerコンテナとして起動する。フロントエンドはAWS_S3上にreactで実装されたソースコードを配置して、前段にCloudFrontを配置する。
イメージは下図。
![react_django.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369619/2da2158b-79f6-c4fe-c04b-8b84a840c5da.png)

また、djangoの導入については今回はさらっと流すことにする。過去にdocker-composeを使ってdjangoGirlsのブログを例に実装した経緯をQiitaへ投稿したので、是非[そちら](https://qiita.com/LittleGreenMen/items/660c27669214da1c9613)を参照いただいた後でこの投稿を読み始めていただきたい。

# Django REST Frameworkとは
Djangoをベースにした、RESTful APIの実装が可能なフレームワーク。
https://www.django-rest-framework.org/
とにかく導入が簡単なので、よくREST APIサーバとして採用されているイメージがある。

# Djangoの導入
まずはPublicSubnet上のEC2で、backend用のディレクトリを作成して、その下に下記3つのファイルを作成する。

```
backend
∟Dockerfile
∟docker-compose.yaml
∟requirements.txt
```

```Dockerfile:Dockerfile
FROM python:3.7
ENV PYTHONUNBUFFERED 1
RUN mkdir /code
WORKDIR /code
ADD requirements.txt /code/
RUN pip3 install -r requirements.txt
ADD . /code/
```

```yaml:docker-compose.yaml
version: '3'
services:
  db:
    image: postgres
    ports:
      - "5432"
    environment:
      - POSTGRES_DB=postgres
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
  django:
    build: .
    command: python manage.py runserver 0.0.0.0:8000
    volumes:
      - .:/code
    ports:
      - "8000:8000"
    depends_on:
      - db
```

```txt:requirements.txt
Django>=2.0,<3.0
psycopg2>=2.7,<3.0
djangorestframework==3.8.2
django-cors-headers==2.4.0
```
DjangoGirlsのblogの例の時と違うのは、requirements.txtにdjangorestframeworkとdjango-cors-headersが追加になっていること。
djnagorestframeworkは今回の主役。
django-cors-headersはS3からリダイレクトで転送されたアクセスを受け付けるために、CORSの設定を行うのに必要になる。

ここまで出来たら、docker-composeを利用してプロジェクトを作成する。
docker-composeコマンドは、Volumesを使っているせいかRootで実行しないとうまくいかなかった。
これはLinux特有の問題で、Dockerコンテナ側とuid/gidが異なるためPermissionErrorが出るというもの。
他に解決策がないでもないが、今回はあくまで検証なので、以降のコマンドはRootで進める。

```shell
sudo su -
```

```shell
docker-compose run django django-admin.py startproject todo_project .
```

↑のコマンドを実行した後のディレクトリ状態は下記の通り。

```
backend
∟Dockerfile
∟docker-compose.yaml
∟requirements.txt
∟manage.py
∟todo_project
　∟__init__.py
　∟settings.py
　∟urls.py
　∟wsgi.py
```

他のファイルを一般ユーザで作っていた場合は、ついでに権限をすべてRootに直しておく。

```shell
sudo chown -R $USER:$USER .
```

DB接続その他のために、settings.pyの下記2か所を書き換える。

```shell
cd todo_project
vi settings.py
```

```python:todo_project/settings.py(28行目あたり)
ALLOWED_HOSTS = ["*"]
```

```python:todo_project/settings.py(76行目あたり)
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'postgres',
        'USER': 'postgres',
        'PASSWORD': 'postgres',
        'HOST': 'db',
        'PORT': 5432,
    }
}
```

アプリケーションを作成する。
(dockerが起動中で残っている場合は、一旦すべて削除する。)

```shell
docker-compose run django python manage.py startapp todos
```

↑のコマンドを実行した後のディレクトリ状態は下記の通り。

```
backend
∟Dockerfile
∟docker-compose.yaml
∟requirements.txt
∟manage.py
∟todo_project
　∟__init__.py
　∟settings.py
　∟urls.py
　∟wsgi.py
∟todos
　∟admin.py
　∟apps.py
　∟__init__.py
　∟migrations
　　∟__init__.py
　∟models.py
　∟tests.py
　∟views.py
```

作成したアプリケーションをsettings.pyに登録する。

```python:todo_project/settings.py(33行目あたり)
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'todos',
]
```

モデルを作成する。
今回はtodoアプリということで、本当にシンプルにtitleとbodyのみを備えたモデルにする。

```python:todos/models.py
from django.db import models

# Create your models here.
class Todo(models.Model):
    title = models.CharField(max_length=200)
    body = models.TextField()

    def __str__(self):
        return self.title
```

作ったモデルでマイグレーションを実行。

```shell
docker-compose run django python manage.py makemigrations todos
```
modelを作成したのでadmin.pyを修正。

```python:admin.py
from django.contrib import admin
from .models import Todo

admin.site.register(Todo)
```

Docker起動時のコマンドでcreatesuperuserがワンライナーで実行できるように、カスタムのコマンドを追加する。

```shell
cd todos
mkdir -p management/commands
touch management/__init__.py
touch management/commands/__init__.py
vi management/commands/createcustomsuperuser.py
```

```python:management/commands/createcustomsuperuser.py
from django.contrib.auth.management.commands import createsuperuser
from django.core.management import CommandError

class Command(createsuperuser.Command):
    help = 'Crate a superuser, and allow password to be provided'

    def add_arguments(self, parser):
        super().add_arguments(parser)
        parser.add_argument(
            '--password', dest='password', default=None,
            help='Specifies the password for the superuser.',
        )

    def handle(self, *args, **options):
        password = options.get('password')
        username = options.get('username')
        database = options.get('database')

        if password and not username:
            raise CommandError("--username is required if specifying --password")

        super().handle(*args, **options)

        if password:
            user = self.UserModel._default_manager.db_manager(database).get(username=username)
            user.set_password(password)
            user.save()
```

migrateとcreatesuperuserを起動時に実行するように、docker-composeのコマンドを書き換える。

```yaml:docker-compose.yaml
    command: sh -c "sleep 5; python manage.py migrate; python manage.py createcustomsuperuser --username root --password 123456 --noinput --email 'blank@email.com'; python manage.py runserver 0.0.0.0:8000"
```

ここまでで基本的なDjangoの設定はできたので、次回からはRESTful APIを提供するための設定を入れていく。

[素人基盤エンジニアがDockerでDjango REST Frameworkとreactを触るシリーズ②：Django REST Framework](https://qiita.com/LittleGreenMen/items/1c09bb1e2241a5d750fa)へつづく