素人基盤エンジニアが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を配置する。
イメージは下図。

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

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
FROM python:3.7
ENV PYTHONUNBUFFERED 1
RUN mkdir /code
WORKDIR /code
ADD requirements.txt /code/
RUN pip3 install -r requirements.txt
ADD . /code/
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
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で進める。

sudo su -
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に直しておく。

sudo chown -R $USER:$USER .

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

cd todo_project
vi settings.py
todo_project/settings.py(28行目あたり)
ALLOWED_HOSTS = ["*"]
todo_project/settings.py(76行目あたり)
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'postgres',
        'USER': 'postgres',
        'PASSWORD': 'postgres',
        'HOST': 'db',
        'PORT': 5432,
    }
}

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

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に登録する。

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のみを備えたモデルにする。

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

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

docker-compose run django python manage.py makemigrations todos

modelを作成したのでadmin.pyを修正。

admin.py
from django.contrib import admin
from .models import Todo

admin.site.register(Todo)

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

cd todos
mkdir -p management/commands
touch management/__init__.py
touch management/commands/__init__.py
vi management/commands/createcustomsuperuser.py
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のコマンドを書き換える。

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

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

素人基盤エンジニアがDockerでDjango REST Frameworkとreactを触るシリーズ②:Django REST Frameworkへつづく