docker-composeでDjangoコンテナ + MySQLコンテナの環境構築 〜 マイグレーションでテーブル作成および管理ツールで確認するまで）。

# 書き直しました！（2019/6/20）
その1とその2に分けて書き直しましたので、こちらへどうぞ！
[Django + MySQLの開発環境をDockerでつくる（その1）](https://qiita.com/Butterthon/items/594168049d930dcad5ae)
[Django + MySQLの開発環境をDockerでつくる（その2）](https://qiita.com/Butterthon/items/08df84cfb1aca230bda5#_reference-ba8dd51863626de19953)
<br/> 
#環境
・macOS Mojave v10.14.3
・Docker v18.09.2

#前提
・Djangoのadminコマンドが使用してプロジェクトおよびアプリの作成ができること
　（`django-admin startproject プロジェクト名`など）

・Djangoの管理コマンドが使用できること
　（`python manage.py makemigrations アプリ名`など）

#ディレクトリ構成
※赤字部分をDjangoコンテナにマウントします。
・startprojectコマンドで「docker_django_app」プロジェクトを作成
　またその配下にstartappコマンドで「django_app」アプリケーションを作成済み
![image.png](https://qiita-image-store.s3.amazonaws.com/0/357470/bbab31e3-46fa-f3a1-dd40-7d680eee9a81.png)

#docker-compose.yml

・djangoサービス配下の「volumes:」でマウントするディレクトリを指定してます。
　コンテナ起動後、コンテナ内の「code」フォルダ配下に指定したディレクトリがマウントされます。（試していませんが、フルパス指定でも大丈夫なはず）

・「build:」に指定したディレクトリ配下のDockerfileがビルドされます。

・DjangoのプロジェクトからmysqlのIPAddressを指定して接続したいので、
固定のIPアドレスが割り振られるようにnetworksを指定してます。

```docker-compose.yml
version: '3'
services:
  mysql:
    image: mysql
    env_file: ./mysql/.env_sample
    command: --default-authentication-plugin=mysql_native_password
    ports:
      - "3306:3306"
    networks:
      app_net:
        ipv4_address: 172.20.0.3
    depends_on:
      - django

  django:
    build: ./django
    ports:
      - "8000:8000"
    volumes:
      - ./docker_django_app:/code
    networks:
      app_net:
        ipv4_address: 172.20.0.2
    tty: true

networks:
  app_net:
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 172.20.0.0/24
volumes:
  est-data:
```

#Dockerfile
WORKDIRにディレクトリを指定すると、コンテナの作業ディレクトリになるので便利。
（execコマンドなどでコンテナに接続した場合に、ここに指定したディレクトリがカレントディレクトリになる。）

```Dockerfile:Dockerfile
FROM python:3.7.2
ENV LANG en_US.utf8
WORKDIR /code
ADD requirements.txt /code
RUN apt-get update
RUN pip install -r requirements.txt
RUN apt-get install -y mysql-client
```

#requirements.txt

```requirements.txt
django==2.1.5
PyMySQL==0.9.3
Pillow
mysqlclient
```

#.env_sample
「redbull」という名前でDBを作成し、そこにDjangoの管理コマンドでマイグレーションを実行してデータベースを更新する予定です。

```.env_sample:.env_sample
MYSQL_ROOT_PASSWORD=password
MYSQL_DATABASE=redbull
MYSQL_USER=db_user
MYSQL_PASSWORD=password

```

#settings.py
必要な部分だけ載せてます。
（ユーザーはrootでよかったかも。）

```settings.py
.
..
...
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django_app',# ←作成したアプリケーションを追加しておく
]
...
..
.
DATABASES = {
    'default': {
        'ENGINE' : 'django.db.backends.mysql'
        , 'NAME' : 'redbull'#←.env_sampleで指定したMYSQL_DATABASE
        , 'USER' : 'db_user'#←.env_sampleで指定したMYSQL_USER
        , 'PASSWORD' : 'password'#←.env_sampleで指定したMYSQL_PASSWORD
        , 'HOST' : '172.20.0.3' #←docker-compose.ymlに指定したmysqlのIPAddress
        , 'PORT' : '3306'
        ,
    }
}
...
..
.
```

#manage.py

```manage.py
import os
import sys
import pymysql

.
..
...
#MySql接続
pymysql.install_as_MySQLdb()
```

#docker-composeでコンテナ起動
まず、`cd`コマンドでdocker-compose.ymlファイルがあるディレクトリまで移動しましょう

以下コマンド実行

```
$ docker-compose up --build -d
```

最終的に以下のようなメッセージが出ていれば起動できている。

```
.
..
...
Creating docker_django_django_1 ... done
Creating docker_django_mysql_1  ... done
```

こんなエラーメッセージが表示された場合、すでに作成済みのコンテナのIPAddress空間や、ホストのIPAddress空間と重複している可能性があるので、docker-compose.ymlに指定した「networks:」の「subnet」を違うアドレスに変更してみてください。

```
Pool overlaps with other one on this address space
```


コンテナが起動しているか確認。
・STATUSが「Up...」になっていたら起動している。

```
$ docker ps
CONTAINER ID        IMAGE                  COMMAND                  CREATED             STATUS              PORTS                               NAMES
9ebfd9bb1af1        mysql                  "docker-entrypoint.s…"   2 minutes ago       Up 2 minutes        0.0.0.0:3306->3306/tcp, 33060/tcp   docker_django_mysql_1
2bdd7ef8dbee        docker_django_django   "python3"                2 minutes ago       Up 2 minutes        0.0.0.0:8000->8000/tcp              docker_django_django_1
```

#コンテナで起動したDjangoの起動画面をホストのブラウザから確認
作成したDjangoコンテナが標準入力を受け入れられるようにします。

```
$ docker exec -it docker_django_django_1 /bin/bash
root@~:/code#
```
カレントディレクトリはDockerfileの`WORKDIR`に指定したディレクトリ（/code）になっている。

**一覧を確認**
ディレクトリ構成で赤字にした部分がちゃんとマウントされている。

```
root@~:/code# ls -al
total 144
drwxr-xr-x  7 root root    224 Mar  9 03:18 .
drwxr-xr-x  1 root root   4096 Mar  9 05:40 ..
-rw-r--r--  1 root root   6148 Mar  6 15:05 .DS_Store
-rw-r--r--  1 root root 131072 Mar  9 01:25 db.sqlite3
drwxr-xr-x 10 root root    320 Mar  9 02:13 django_app
drwxr-xr-x  7 root root    224 Mar  9 03:18 docker_django_app
-rwxr-xr-x  1 root root    606 Mar  9 01:34 manage.py
root@~:/code#
```

Djangoのサーバーを起動してホストから接続してみる

```
.
..
...
root@~:/code# python manage.py runserver 0.0.0.0:8000
Performing system checks...

System check identified no issues (0 silenced).

You have 16 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, django_app, sessions.
Run 'python manage.py migrate' to apply them.

March 09, 2019 - 06:46:34
Django version 2.1.5, using settings 'docker_django_app.settings'
Starting development server at http://0.0.0.0:8000/
Quit the server with CONTROL-C.
```

起動できたので、ホストのブラウザに以下URLを打ち込んでアクセスして、
ロケットが飛んでるWebページが表示されれば成功です。
おめでとうございます。
（Djangoのバージョンによって違うかもしれないですが。）
`http://localhost:8000`
<img width="655" alt="image.png" src="https://qiita-image-store.s3.amazonaws.com/0/357470/bf091f17-ef7a-d02b-264e-efeed02b5210.png">


#マイグレーションする（テーブル作成）
コンテナにマウントしているので、ホスト側でソースを編集しても問題ありません。
**models.py**を編集しましょう。
（コピペでもよいですし、独自に作っていただいても構いません。）

``` models.py
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User

# Create your models here.
'''
お酒テーブル
'''
class Alcohol(models.Model):
    product_name = models.CharField(max_length=256)
    internal_capacity = models.IntegerField(default=0)
    price = models.IntegerField(default=0)
    frequency = models.FloatField(default=0)
    origin_country = models.CharField(max_length=256, blank=True)
    description = models.TextField(max_length=4000, blank=True)
    delete_flag = models.CharField(max_length=1, default='0')

    def __str__(self):
        return 'ID: ' + str(self.id) + ', お酒の名前: ' + self.product_name + '(' + str(self.internal_capacity) + ')'

'''
消費税テーブル
'''
class Tax(models.Model):
    tax = models.FloatField()

'''
お酒の写真
'''
class AlcoholPicture(models.Model):
    alcohol = models.ForeignKey(Alcohol, on_delete=models.CASCADE)
    picture = models.ImageField(upload_to='images/')
```


モデルができたので、マイグレーションファイルを作成しましょう。
（コマンド実行はDjangoのコンテナ内から行ってくださいね）
`python manage.py make migrations <アプリの名前>`

```
root@~:/code# python manage.py makemigrations django_app
Migrations for 'django_app':
  django_app/migrations/0001_initial.py
    - Create model Alcohol
    - Create model AlcoholPicture
    - Create model Tax
root@~:/code#
```

このままマイグレーションを実行します。
実行すると、作成したマイグレーションファイルを適用してデータベースが更新されます。

```
root@~:/code# python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, django_app, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying django_app.0001_initial... OK
  Applying sessions.0001_initial... OK
root@~:/code#
```


#管理ツールを使用するために、管理ユーザーを作成する
まず管理者を作成します。
・Username:admin にしました
・Email address：適当に。
・Password：適当に。
・Password (again)：適当に。
パスワードを簡単にすると、パスワードこのままでいいですかみたいな確認をされますが、
ローカルで確認したいだけなのでとりあえず"y"で続けちゃいましょう。

```
root@~:/code# python manage.py createsuperuser
Username (leave blank to use 'root'): admin
Email address: admin@admin.com
Password:
Password (again):
The password is too similar to the email address.
This password is too short. It must contain at least 8 characters.
This password is too common.
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.
root@~:/code#
```

#管理ツールで利用できるテーブルを登録
さきほどマイグレーションで登録したテーブルを管理ツールで利用できるよう、
**admin.py**を編集しましょう。

```admin.py
from django.contrib import admin
from .models import Alcohol, Tax

# Register your models here.
admin.site.register(Alcohol)
admin.site.register(Tax)
```

これだけでOK


#管理ツールにログインしてみる。
Djangoコンテナでサーバーを起動し、管理ツールにアクセスしてみる。
`http://localhost:8000/admin`

<img width="428" alt="image.png" src="https://qiita-image-store.s3.amazonaws.com/0/357470/5a8bce5a-a7f9-0c84-255f-2cebe7c07175.png">

さきほど作成した管理者ユーザーでログイン。
<img width="655" alt="image.png" src="https://qiita-image-store.s3.amazonaws.com/0/357470/98355740-272b-f04c-21c8-3d0a74d8bdaa.png">

**admin.py**に記載したモデルが登録されているが確認できました。
以上です。お疲れ様でした。


#備考
殴り書きなので大変みづらいと思います、申し訳ありません。
パートを分ければよかったと反省（・_・；）

dockerでDjangoからMySQLに接続できるようになったからホスト環境を汚すことなくあれこれできる・・・便利（感動）