Edited at

Docker上にDjangoとReactの環境を構築する

学習履歴


■はじめに

現在、Docker と Django を使ったシステム開発案件に参画している。

バックエンドは Django だが、フロントエンドは、javascript と jQuery を使っている。

フロントエンドも React とか Vue.js などのフレームワークで開発できないかと思って、Django + React + Docker の環境を作ることにした。


■事前準備

事前準備として、プロジェクトフォルダと以下のファイルを作成する。

mkdir todoapp_with_docker

cd todoapp_with_docker

mkdir app

touch Dockerfile
touch Dockerfile-nodejs
touch Makefile
touch docker-compose.yml
touch requirements.txt

各ファイルに以下の設定を行う。


docker-compose.yml

version: "3"

services:
app:
build:
context: .
ports:
- "8000:8000"
volumes:
- ./app:/app
command: >
sh -c "python manage.py migrate &&
python manage.py runserver 0.0.0.0:8000"
environment:
- DB_HOST=db
- DB_NAME=app
- DB_USER=postgres
- DB_PASS=supersecretpassword
depends_on:
- db
- react

db:
image: postgres:10-alpine
environment:
- POSTGRES_DB=app
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=supersecretpassword

react:
build:
context: .
dockerfile: "./Dockerfile-nodejs"
volumes:
- ./frontend:/frontend
command: >
sh -c "cd frontend && npm start"
ports:
- "3000:3000"



Dockerfile

FROM python:3.7-alpine

MAINTAINER kanagawa App Developer Ltd

ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

COPY ./requirements.txt /requirements.txt
RUN apk add --update --no-cache postgresql-client jpeg-dev
RUN apk add --update --no-cache --virtual .tmp-build-deps \
gcc libc-dev linux-headers postgresql-dev musl-dev zlib zlib-dev
RUN pip install -r /requirements.txt
RUN apk del .tmp-build-deps

RUN mkdir /app
WORKDIR /app
COPY ./app /app

RUN mkdir -p /vol/web/media
RUN mkdir -p /vol/web/static
RUN adduser -D user
RUN chown -R user:user /vol/
RUN chmod -R 755 /vol/web
USER user



Dockerfile-nodejs

FROM node:10.13-alpine

MAINTAINER kanagawa App Developer Ltd

RUN npm install -g create-react-app
RUN npm install axios



Makefile

.PHONY: app test migrate pro admin django react 

app:
docker-compose run --rm app sh -c "python manage.py startapp ${app}"

test:
docker-compose run --rm app sh -c "pytest -l -v -s ${app} && flake8"

migrate:
docker-compose run --rm app sh -c "python manage.py makemigrations"
docker-compose run --rm app sh -c "python manage.py migrate"

pro:
docker-compose run --rm app sh -c "django-admin startproject ${pro} ."

admin:
docker-compose run --rm app sh -c "python manage.py createsuperuser"

django:
docker-compose run --rm app sh -c "django-admin startproject app ."

react:
docker-compose run --rm react sh -c "create-react-app frontend"



requirements.txt

Django>=2.1.3,<2.2.0

djangorestframework>=3.9.0,<3.10.0
flake8>=3.6.0,<3.7.0
psycopg2>=2.7.5,<2.8.0
Pillow>=5.3.0,<5.4.0
pytest-django==3.5.1
django-cors-headers==2.4.0

Dockerfile や requirements.txt に本アプリには不要な設定やモジュールが入っているが(pytest-djangoなど)、書いておくと後々他のアプリケーション作成時に役立つので、記載している。

ここまでのファイル構成は、以下のようになる。

$ tree

todoapp_with_docker
├── Dockerfile
├── Dockerfile-nodejs
├── Makefile
├── app
├── docker-compose.yml
└── requirements.txt

ビルドを行う。

docker-compose build

Django のプロジェクトを作成する。

make django

React プロジェクトを作成する。

make react

プロジェクトを起動する。

docker-compose up

Django -> localhost:8000 にアクセスする。

React -> localhost:3000 にアクセスする。

それぞれのスタート画面が出てきたら成功。


■Todo アプリの作成

todos アプリを作成する。

make app=todos

以下のファイルを変更する。


app/settings.py

INSTALLED_APPS = [

# Local
'todos.apps.TodosConfig',

# 3rd-party
'rest_framework',
'corsheaders',

'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]

MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',# 追加
'django.middleware.common.CommonMiddleware',# 追加
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

CORS_ORIGIN_WHITELIST = (
'localhost:3000/'
)

# 追加
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.AllowAny',
]
}



todos/models.py

from django.db import models

class Todo(models.Model):
title = models.CharField(max_length=200)
body = models.TextField()

def __str__(self):
return self.title



todos/admin.py

from django.contrib import admin

from .models import Todo

admin.site.register(Todo)


管理者を作成する。

make admin

Username (leave blank to use 'user'): admin
Email address:
Password:
Password (again):
Superuser created successfully.


todos/admin.py

from django.contrib import admin

from .models import Todo

admin.site.register(Todo)


マイグレーションを実行する。

make migrate

localhost:8000/admin にアクセスして、データを 3 つほど作成しておく。

ルーティングの設定を行う。


app/urls.py

from django.contrib import admin

from django.urls import include, path

urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('todos.urls')), # 追加
]


todos 配下に urls.py を作成する。

touch app/todos/urls.py


todos/urls.py

from django.urls import path

from .views import ListTodo, DetailTodo

urlpatterns = [
path('<int:pk>/', DetailTodo.as_view()),
path('', ListTodo.as_view()),
]


シリアライズファイルを用意する。

touch app/todos/serializers.py


todos/serializers.py

from rest_framework import serializers

from .models import Todo

class TodoSerializer(serializers.ModelSerializer):
class Meta:
model = Todo
fields = ('id', 'title', 'body',)


View の設定を行う。


todos/views.py

from rest_framework import generics

from .models import Todo
from .serializers import TodoSerializer

class ListTodo(generics.ListAPIView):
queryset = Todo.objects.all()
serializer_class = TodoSerializer

class DetailTodo(generics.RetrieveAPIView):
queryset = Todo.objects.all()
serializer_class = TodoSerializer


Webサーバーを立ち上げ直して、http://localhost:8000/api 及び http://localhost:8000/api/1/ にアクセスする。

表示されたら成功。


■React アプリの作成

React 側の実装を行う。


frontend/src/App.js

import React, { Component } from 'react';

import axios from 'axios';

class App extends Component {
state = {
todos: []
};

componentDidMount() {
this.getTodos();
}

getTodos() {
axios
.get('http://localhost:8000/api/')
.then(res => {
this.setState({ todos: res.data });
})
.catch(err => {
console.log(err);
});
}

render() {
return (
<div>
{this.state.todos.map(item => (
<div key={item.id}>
<h1>{item.title}</h1>
<p>{item.body}</p>
</div>
))}
</div>
);
}
}

export default App;


http://localhost:3000/ にアクセスし、Django のデータベースに保存したデータが表示されたら成功。

■ 終わりに

docker を使うとスムーズに環境構築ができていい感じだった。

ただ、この方法だと Django を Web API にしないと使いずらそう。

Django を、Webアプリケーションサーバーみたい使っているので、この変更は無理かもしれない。