(初心者向け）DjangoのDocker開発環境を作成する。

## はじめに
こんにちはmamedogといいます。
少し前にハッカソンでDjangoを用いたアプリケーションを作成しました。
その際に得た知見を、共有できればと思い記事にできればと思います。

## 構成
今回作成する開発環境は以下のような環境を作成しようと思います。
※ちなみに私はMac環境ですが、Dockerを使うのでWindowsでも同様の環境を構築可能だと思います。

- Macのローカル環境のDocker上で開発をできるようにする
- データベースはMySQLを使う

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/499266/cd11d3f2-5ce3-35cf-f4a4-7c35d3448be4.png)

# Dockerを使うと何が嬉しいのか
※不要な方は読み飛ばしてください。
開発するにあたりDockerを使う前提で話を進めていますが、そもそもDockerを使うと何が嬉しいのでしょうか。
個人天気な見解ではありますが下記のような恩恵を得られると思っています。

- Macのローカル環境に直接ソフトウェアをインストールしなくて良い（環境を汚さずに済む）
- 実行環境による環境差分の影響を少なく抑えることができる

### Macのローカル環境に直接ソフトウェアをインストールしなくて良い
Dockerを使うことにより、Macに直接Pythonや、MySQL等のソフトウェアをインストールする必要がなくなります。
直接Macにインストールをしてしまうと、今回作るアプリケーションはPython3.9を使いたいけれど、次回使うアプリケーションはPython3.12を使いたい。等の場合に、アプリケーションを実行するたびに、Pythonのバージョンを切り替えなければならないという手順が発生します。
また、上記の例で、Python3.9が不要になった場合はアンインストールをする等の対応をしたほうが良いかと思いますが、様々なアプリケーションを作るたびに管理コストが増大していきます。
Dockerを使うことにより、アプリケーションと、Python環境をセットで管理し、必要な時だけ作成し、不要になったらコンテナを削除するという運用が可能になるので、Macに不要なソフトウェアが増えていくこともなくなります。

### 実行環境による環境差分の影響を少なく抑えることができる
ローカルのMac上でPythonをインストールし、アプリケーションを直接動かすと、AさんのMacではうまく動くが、BさんのWindowsではうまくアプリケーションが動かない。という事態が発生する可能性があります。
Dockerコンテナは、OS毎別の環境を作成することができるため、別のパソコンや、サーバー上でコンテナを起動した場合でも基本的には環境による影響が発生しないように作られています。
そのため、AさんのMac上で開発したアプリケーションをBさんのWindows上で起動した場合でも環境起因でうまく動かないという辞退が発生しづらいです。
また、クラウド環境等にデプロイを行った場合でも、同様に実行環境による影響を少なくすることができます。

※下記の３つの環境は全て同じコンテナを起動しているため環境による影響が発生する可能性を低く抑えることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/499266/307f0346-a667-a300-504e-e5e664964faa.png)

## 実際に構築していく
前置きはこれくらいにして、実際にDocker及び、Djangoの環境を構築していこうと思います。
基本的な指針としては、公式ドキュメントに沿って構築を進めていければと思います。

https://docs.djangoproject.com/ja/5.1/

### Python環境を準備し、Djangoをインストールする
Djangoをインストールするために、Python環境を用意しなければなりません。
基本的には下記のインストールガイドに沿って進めていけば問題ないかと思いますが、念の為順を追って解説していきます。

https://docs.djangoproject.com/ja/5.1/intro/install/

#### 必要なPythonのバージョンを確認する
インストールするDjangoのバージョンによって、必要となるPythonのバージョンも変わってくるため、そのあたりの情報を確認します。
下記のページから確認してください。

https://docs.djangoproject.com/ja/5.1/faq/install/#faq-python-version-support

今回は現時点での最新バージョンのDjango（5.1)をインストールしようかと思うので、Pythonについては、	`3.10, 3.11, 3.12, 3.13`のどれかになりそうです。
基本的にはどれでも問題ないと思うのですが、今回は`3.12`を選択していきます。

#### dockerhub上で利用するPythonのdockerImageを確認する
インストールしたいPythonのバージョンが決まったので、該当のPythonを利用できるDockerImageを確認します。
DockerImageについては、メジャーなプログラミング言語でしたら基本的に公式がImageを配布しているのでそれを利用します。Pythonの場合は下記のページに記載されています。

https://hub.docker.com/_/python

上記のページの`Supported tags and respective Dockerfile links`の部分に、配布されているPythonのDockerImageの種類が記載されているので、その中から選択していきます。
今回は`Python3.12`のイメージが欲しいので、`3.12`と記載がされているものを選びます。

:::note info
バージョン`3.12`のようなバージョンの情報以外にも、`alpine` `bullseye` `slim` 等の英単語が書いてあって正直選びずらいかと思いますが、これらはPythonがインストールされているベースとなっているOSの違いになります。

ここでは詳しい説明は割愛しますが、「docker image 選び方」等でgoogle検索すると、そのあたりの情報を記載している記事が出てくるかと思うので、わからなければ調べてみてください！
:::

後ほどでてくるmysqlclientのインストール等も鑑みて今回は`3.12.8-slim-bookworm`を使っていこうと思います。

### mysqlについても同様にImageを確認する
上記のPythonと同様にmysqlについても公式からImageが配布されているのでインストールするものを確認していきます。

https://hub.docker.com/_/mysql


Pythonの場合と考え方は同じなので詳細は割愛しますが、今回は下記を利用していこうと思います。

`9.1.0`

#### DockerFile、docker-compose.ymlを作成
実際に上記で調べたPython及びmysqlのコンテナを手元のMac環境で起動して行くための設定を書いていきます。

:::note warn
今回の記事につきましては、Dockerアプリケーションが手元のPCにインストールされている前提で進めさせて頂ければと思います。
Dockerアプリケーションのインストールについては、公式を見て頂くか、「docker インストール」等で検索してインストールしてみてください！

- 公式
https://www.docker.com/products/docker-desktop/
:::

手元のコンソールでdockerコマンドとdocker-composeコマンドがインストールされていることを確認します。
``` shell:shell
$ docker -v                                                               
Docker version 27.4.0, build bde2b89

$ docker-compose -v
Docker Compose version v2.31.0-desktop.2
```

アプリケーションを管理するディレクトリを作成します。
名前はプロジェクトによって異なるかと思いますが、今回は公式のチュートリアルに倣って`djangotutorial` とします。

https://docs.djangoproject.com/ja/5.1/intro/tutorial01/

```shell:shell
$ mkdir djangotutorial
```

インフラの関連のファイルを管理するディレクトリを作成します。
```shell:shell
# インフラ関連のファイルを管理するディレクトリ
$ mkdir djangotutorial/infra
# アプリケーション関連のインフラファイルを管理するディレクトリ
$ mkdir djangotutorial/infra/app
# データベース関連のインフラファイルを管理するディレクトリ
$ mkdir djangotutorial/infra/db
# データベースの実データを格納するディレクトリ
$ mkdir djangotutorial/infra/db/db_data
```
現在の構造は下記です。
```shell:shell
$ tree djangotutorial                                                     
djangotutorial
└── infra
    ├── app
    └── db
        └── db_data
```

Python環境用のコンテナとmysql用のコンテナそれぞれについて、Dockerfileを作成します。

:::note warn
今回の記事につきましては、Dockerの詳しい内容の説明等は行わないため、Dockerfile,docker-compose.ymlの詳しい理解につきましては、google検索等で調べてみてください！

知り合いの方が詳しく解説してくださっているので、必要に応じてこちらもご確認ください！
https://qiita.com/ikemura-ren/items/b961642d55e4e6515203

:::


```Dockerfile:djangotutorial/infra/app/Dockerfile
# 先ほど調べたPythonのDockerImageのタグを記載する
FROM python:3.12.8-slim-bookworm
```
```Dockerfile:djangotutorial/infra/db/Dockerfile
# 先ほど調べたmysqlのDockerImageのタグを記載する
FROM mysql:9.1.0
```
Dockerfileに書くことができる各項目については、下記の公式リファレンスを参考にしてください。

https://docs.docker.jp/engine/reference/builder.html

※まずは起動することを目標としたいので一旦上記の記述だけでOKです。後ほど必要な内容は追記できればと思います。

続いて`docker-compose.yml`も作成していきます。
```yaml:djangotutorial/docker-compose.yml
services:
  app:
    build:
      context: .
      dockerfile: ./infra/app/Dockerfile
    working_dir: '/usr/src/app'
    tty: true
    volumes:
      - ./:/usr/src/app
    ports:
      - 8080:8000
  db:
    build:
      context: .
      dockerfile: ./infra/db/Dockerfile
    tty: true
    volumes:
      - ./infra/db/db_data:/var/lib/mysql
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
```

こちらも各項目の詳しい内容については、下記のdocker-composeのリファレンスを参考にして頂ければと思います。

https://docs.docker.jp/compose/compose-file/index.html

重要な項目だけ解説させていただくと、
- ports
ローカルPCのポートと、コンテナのportを接続する項目です。
`app`の｀ports`に`- 8080:8000`と記載されているので、コンテナ上の8000番ポートでアプリケーションを起動した際にmacのブラウザのlocalhost:8080にアクセスするとコンテナ上のアプリケーションに接続することができます。

- volumes
ローカルのPCのディレクトリと、Dockerコンテナ上のディレクトリを同期させる項目です。
`app`の`volumes`に`- ./:/usr/src/app`と記載しているのは、ローカルのPC上でファイル（ソースコード）を編集したときに自動的にコンテナ上で動いているコードも更新されることを目的としています。
`db`の`volumes`に`- ./infra/db/db_data:/var/lib/mysql`と記載されているのは、データベースに保存した値をローカルのPCに保存しておくことにより、コンテナを再作成した場合でもデータベースの値をリセットされないようにすることを目的としています。

- environment
起動するコンテナに環境変数を設定する項目です。
`db`の`environment`に`MYSQL_ROOT_PASSWORD`を記載しているのは、mysqlのコンテナは、事前にrootユーザーのパスワードについて設定をする必要があるようで、
`MYSQL_ROOT_PASSWORD`,`MYSQL_ALLOW_EMPTY_PASSWORD`,`MYSQL_RANDOM_ROOT_PASSWORD`のいずれかを記載しないと起動できない仕様となっているためです。
右側の${MYSQL_ROOT_PASSWORD}は、ローカルPCの環境変数を参照する記載です。

上記で環境変数を参照する記載をしたので、環境変数を設定する`.env`ファイルを作成します。
```.env:djangotutorial/.env
MYSQL_ROOT_PASSWORD=my-secret-pw
```
ここではrootユーザーのパスワードを`my-secret-pw`としていますが、実際はご自身で管理するパスワードを設定してください。


:::note alert
.envファイルについては上記のようにパスワード等機密性の高い情報を扱う場合が多いので、基本的に他人に共有しないようにしてください。

プロジェクトをgit管理しているような場合には、.gitignoreに記載するなどして、他人から見えないようにすることをおすすめします。
:::


#### Dockerコンテナを起動する
上記で一旦コンテナを起動する準備は整ったので、下記のコマンドでDockerコンテナを起動していきます。
```shell:shell
# ディレクトリを移動
$ cd djangotutorial

# docker-compose起動コマンド
$ docker-compose up -d
```

上記コマンド実行後、しばらくするとdockerコンテナの構築が完了するかと思いますので、起動がうまく行っているか下記のコマンドで確認します。
下記のように２行分（コンテナ２つ分）の表示がでれば成功していると思います。
```shell:shell
$ docker ps

CONTAINER ID   IMAGE                COMMAND                   CREATED          STATUS          PORTS                               NAMES
xxxxxxxxxxxx   djangotutorial-db    "docker-entrypoint.s…"   11 minutes ago   Up 11 minutes   0.0.0.0:3306->3306/tcp, 33060/tcp   djangotutorial-db-1
xxxxxxxxxxxx   djangotutorial-app   "python3"                 16 minutes ago   Up 16 minutes   0.0.0.0:8080->8000/tcp              djangotutorial-app-1
```

### Djangoをインストールする
ここまでで、ようやくDjangoアプリケーションを動かすベースとなる、Python環境とMySQL環境を用意することができましたので、Djangoをインストールして行きたいと思います。
DjangoについてはPython環境で実行する必要があるので、まずappコンテナの中に入って作業をしようと思います。
コンテナに入るコマンドは下記です。
```shell:shell
# appコンテナにおいて、bashコマンドを実行する
$ docker-compose exec app bash
root@xxxxxxxx:/usr/src/app#
```
上記のようにローカルPCとは別のコマンドラインが立ち上がれば成功です。
念の為、先程インストールしたバージョンのpythonが使えることを確認します。

```shell:shell(appContainer)
root@xxxxxxxx:/usr/src/app# python --version
Python 3.12.8
```


Pythonを実行できるlinux環境にログインすることができたので、ここからは公式のチュートリアルを参考に進めていきます。



公式のとおりにpipを使ったインストールコマンドを実行していきます。
pipでPythonパッケージを追加する場合、仮想環境上へのインストールが推奨されているため、公式に記載のある方法で仮想環境を作成・有効化していきます。

https://docs.djangoproject.com/ja/5.1/intro/contributing/

:::note info
仮想環境の要否について
今回構築する環境はDocker環境のため、仮想環境の作成すべきか迷いましたが、PEP668（https://peps.python.org/pep-0668/ )をみるとDocker環境下でも仮想環境を構築したほうがより安全と言えそうでしたので、venvを使う方針で記載しています。
とは言え、PEP668にもDocker環境下でアプリケーションを１つだけ動かしている状況下であれば、直接pipを用いたinstallを実行してもリスクは低いとの記載もありましたので、venvを利用せずともすぐには問題なさそうかと思います。
:::

```shell:shell(appContainer)
# 仮想環境作成
root@xxxxxxxxxxxx:/usr/src/app# python -m venv ~/.virtualenvs/djangodev
# 仮想環境有効化
root@xxxxxxxxxxxx:/usr/src/app# source ~/.virtualenvs/djangodev/bin/activate
(djangodev) root@xxxxxxxxxxxx:/usr/src/app#
```
その後下記の公式の記載にに従い、Djangoをインストールします。

https://docs.djangoproject.com/ja/5.1/topics/install/#installing-official-release

```shell:shell(appContainer)
(djangodev) root@xxxxxxxxxxxx:/usr/src/app# python -m pip install Django
```

公式と同様にインストールが成功していることを確認します。
```shell:shell(appContainer)
(djangodev) root@xxxxxxxxxx:/usr/src/app# python
Python 3.12.8 (main, Dec 25 2024, 17:46:39) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import django
>>> print(django.get_version())
5.1.4
```

こちらのようにDjangoのバージョンが表示されればインストールが成功しています。
pipによってモジュールを追加したので、次回起動したときにも同じ環境が作れるように`requirements.txt`に書き出します。
```shell:shell(appContainer)
(djangodev) root@xxxxxxxxxxxx:/usr/src/app# pip freeze > requirements.txt
(djangodev) root@xxxxxxxxxxxx:/usr/src/app# ls
docker-compose.yml  infra  requirements.txt
```
### Djangoのプロジェクトを作成する。
Djangoのインストールまでは完了したので、引き続き公式チュートリアルの流れに沿ってプロジェクトを作成していきます。

https://docs.djangoproject.com/ja/5.1/intro/tutorial01/

`djangotutorial`という名前のディレクトリを作るところまでは完了しているので、プロジェクトを作成する下記のコマンドから実行していきます。
公式に倣ってプロジェクト名は`mysite`とさせていただきますが、ここはプロジェクトによって任意に変更頂ければと思います。
下記のように、`manage.py` と`mysite`ディレクトリが追加されていればうまくいっています。

```shell:shell(appContainer)
(djangodev) root@xxxxxxxxxxxx:/usr/src/app# django-admin startproject mysite .
(djangodev) root@xxxxxxxxxxxx:/usr/src/app# ls
docker-compose.yml  infra  manage.py  mysite  requirements.txt
```
※公式では、`djangotutorial`ディレクトリの外側で作業しているため`django-admin startproject mysite djangotutorial`となっていますが、ここでは`djangotutorial`ディレクトリ中で作業しているので、少しコマンドを変更しています。

#### ブラウザから接続する
プロジェクトの作成が完了したので、公式の流れに沿って、開発用のアプリケーションサーバーを起動し、PCのブラウザからアクセスしてみます。

まず、アプリケーションサーバーを立ち上げます。起動コマンドは下記です。
※Dockerで起動する場合、macのブラウザからのアクセスは別のマシンからのアクセスとなるため、`0.0.0.0`をつける必要があるようです。

```shell:shell(appContainer)
(djangodev) root@xxxxxxxxxxxx:/usr/src/app# python manage.py runserver 0.0.0.0:8000
```
`Starting development server at http://0.0.0.0:8000/`の記載が出ていればおそらく起動は成功しているので、下記のようにブラウザからアクセスしてみます。
ブラウザからアクセスする場合はローカルPCのポートの`8080`とコンテナの`8000`をdocker-composeで紐づけているので、`http://localhost:8080/`にアクセスします。

Djangoおなじみのロケットの画面が表示されれば成功です。

![スクリーンショット 2025-01-14 13.03.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/499266/2c97dd7a-d7ac-f805-3461-efbac9668066.png)


アプリケーションサーバーを立ち上げることまで完了したので、これ以降については、公式のチュートリアルを参考に開発を進めて頂くのがよいと思います。

https://docs.djangoproject.com/ja/5.1/intro/tutorial01/#creating-the-polls-app

### データベース（MySQL)を接続する
最後に公式のチュートリアルでは、データベースに`MySQL`ではなく、`sqlite3`を使っているため、MySQLの接続方法だけ記述できればと思います。

データベースの設定は`mysite/settings.py`に記載がされています。
デフォルトでは下記の記載になっています。

```python:mysite/settings.py
DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.sqlite3",
        "NAME": "mydatabase",
    }
}
```

先ほど立ち上げたMySQLコンテナに接続するためにはこの部分を下記のリファレンスを参考に下記の記載に変更します。

https://docs.djangoproject.com/en/5.1/ref/settings/#databases

```python:mysite/settings.py
DATABASES = {
    "default": {
        # 利用するデータベースの種類を記載　　
        "ENGINE": "django.db.backends.mysql",
        # mysql上のデータベースを選択
        "NAME": "sample_db",
        # mysql上のユーザーを選択
        "USER": "sample_user",
        # 上記ユーザーのパスワードを記載
        "PASSWORD": "sample_pass",
        # データベースのホスト名を記載（docker-composeで作成した場合、コンテナ名でアクセスできる）
        "HOST": "db",
        # 対象サーバー（コンテナ）のポート番号を記載
        "PORT": "3306",
    }
}
```

ここで、問題となるのが、MySQLサーバーについては、作ったばかりで、データベースの作成や、ユーザーの作成等がされていません。
そのため、`NAME`,`USER`,`PASSWORD`等の値を記載できないため、MySQL側でこれらを作成していきます。
作成する方法は何通りかあるかと思いますが、今回は公式のDockerImageに記載がある方法で、環境変数を設定することにより、作成していこうと思います。

https://hub.docker.com/_/mysql

上記を見ると、`MYSQL_DATABASE`, `MYSQL_USER`, `MYSQL_PASSWORD`を環境変数に設定する事により、データベースとユーザーが作成されることがわかります。

> MYSQL_DATABASE
This variable is optional and allows you to specify the name of a database to be created on image startup. If a user/password was supplied (see below) then that user will be granted superuser access (corresponding to GRANT ALL⁠) to this database.
> MYSQL_USER, MYSQL_PASSWORD
These variables are optional, used in conjunction to create a new user and to set that user's password. This user will be granted superuser permissions (see above) for the database specified by the MYSQL_DATABASE variable. Both variables are required for a user to be created.



これに従い、`docker-compose.yml`の`db`の`environment`に下記を追記します。

```yaml:djangotutorial/docker-compose.yml
services:
  app:
    build:
      context: .
      dockerfile: ./infra/app/Dockerfile
    working_dir: '/usr/src/app'
    tty: true
    volumes:
      - ./:/usr/src/app
    ports:
      - 8080:8000
  db:
    build:
      context: .
      dockerfile: ./infra/db/Dockerfile
    tty: true
    volumes:
      - ./infra/db/db_data:/var/lib/mysql
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      #ここから追記！
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
```

docker-compose.ymlもmacの環境変数を参照するように記載しているので、.envファイルに下記を追記します。

```env:djangotutorial/.env
MYSQL_ROOT_PASSWORD=my-secret-pw

# ここから追記！
MYSQL_DATABASE=sample_db
MYSQL_USER=sample_user
MYSQL_PASSWORD=sample_pass
```
※`MYSQL_DATABASE`, `MYSQL_USER`, `MYSQL_PASSWORD`については、`settings.py`の`NAME`,`USER`,`PASSWORD`と一致している必要があります。　値自体は任意なので、ご自身で考えて設定してください。

これらの設定については、データベースが初期化されるタイミングで読み込まれるので一度データベースの内容を削除し、コンテナを再度作成します。
データベースの実データについては、`djangotutorial/infra/db/db_data`に格納するように設定していますので下記のコマンドで削除します。
```shell:shell(localPC)
$ pwd                                                           
/xxx/xxx/xxx/djangotutorial

$ rm -rf infra/db/db_data/*
sure you want to delete all 29 files in /xxx/xxx/xxx/djangotutorial/infra/db/db_data [yn]? y
```
こちらでデータベースの実データを削除したので、コンテナを再度作成します。

```shell:shell(localPC)
$ docker-compose down
[+] Running 3/3
 ✔ Container djangotutorial-app-1  Removed                                                                                                           10.2s
 ✔ Container djangotutorial-db-1   Removed                                                                                                            1.2s
 ✔ Network djangotutorial_default  Removed


 $ docker-compose up -d
 [+] Running 3/3
 ✔ Network djangotutorial_default  Created                                                                                                            0.0s
 ✔ Container djangotutorial-app-1  Started                                                                                                            0.3s
 ✔ Container djangotutorial-db-1   Started
 ```
こちらでデータベースが再作成されたので、データベースのコンテナに入り、ユーザーとデータベースが作成されているかを確認します。

```shell:shell
$ docker-compose exec db bash
bash-5.1#
bash-5.1# mysql -usample_user -psample_pass 

<中略＞

mysql>
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| performance_schema |
| sample_db          |　←これが作成されている。
+--------------------+
3 rows in set (0.03 sec)
```

上記のようにコンテナの中に入り、設定したユーザー、パスワードでmysalClientを起動し、設定したデータベースが作成されていれば成功です。

これでデータベース側の準備は整ったので、アプリケーションのコンテナから接続できることを確認します。

データベースコンテナから外に出ます。
```shell:shell(dbContainer)
mysql> exit
Bye
bash-5.1# exit
exit
```

アプリケーションコンテナの中にはいります。
```shell:shell
$ docker-compose exec app bash                                            
root@xxxxxxxxxxxxx:/usr/src/app#
```

アプリケーションコンテナ内でマイグレーションコマンドを実行します。マイグレーションコマンドについては下記に記載があります。

https://docs.djangoproject.com/ja/5.1/intro/tutorial02/

先ほどコンテナを作り直してしまい、その際にインストールしたdjango等も消えているので再度インストールします。
また、コンテナ作り直しにより仮想環境も消えているため、再度作成・有効化します。

```shell:shell(appContainer)
# 仮想環境作成
root@xxxxxxxxxxxx:/usr/src/app# python -m venv ~/.virtualenvs/djangodev
# 仮想環境有効化
root@xxxxxxxxxxxx:/usr/src/app# source ~/.virtualenvs/djangodev/bin/activate
# 先ほどインストールしたモジュールを再度インストール
(djangodev) root@xxxxxxxxxxxxx:/usr/src/app# pip install -r requirements.txt
```

マイグレーションコマンドを実行します。

```shell:shell(appContainer)
(djangodev) root@xxxxxxxxxxxxx:/usr/src/app# python manage.py migrate
<中略>
django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module.
Did you install mysqlclient?
```
エラーになります。実は、DjangoでMySQLに接続するためには、python環境にmysqlclientをインストールする必要があります。
そのため、これをインストールします。
この点については、下記に記載があります。

https://docs.djangoproject.com/ja/5.1/ref/databases/#mysql-notes

上記のドキュメントを見ると
> mysqlclient
> Django には mysqlclient 1.4.3 以降が必要です。

と記載があります。そのためこれをインストールします。
下記のページにインストール方法の説明があります。

https://pypi.org/project/mysqlclient/

こちらのドキュメントのLinuxの部分を見ると、

>Note that this is a basic step. I can not support complete step for build for all environment. If you can see some error, you should fix it by yourself, or ask for support in some user forum. Don't file a issue on the issue tracker.
>You may need to install the Python 3 and MySQL development headers and libraries like so:
> $ sudo apt-get install python3-dev default-libmysqlclient-dev build-essential pkg-config # Debian / Ubuntu
% sudo yum install python3-devel mysql-devel pkgconfig # Red Hat / CentOS
Then you can install mysqlclient via pip now:

と記載がされています。
英語で記載されていて難しいですが、pipでmysqlclientをインストールする前提として、OS(linux)側のモジュールとして`python-3dev`等のモジュールをインストールする必要があると言っています。
今回はDevian系のLinuxを利用しているので下記のコマンドでインストールしていきます。
```shell:shell(appContainer)
(djangodev) root@xxxxxxxxxxxxx:/usr/src/app# apt-get update
(djangodev) root@xxxxxxxxxxxxx:/usr/src/app# sudo apt-get install python3-dev default-libmysqlclient-dev build-essential pkg-config
```

これを実行すると、pipでmysqlclientがインストールできるようになります。
```shell:shell(appContainer)
(djangodev) root@xxxxxxxxxxxxx:/usr/src/app# pip install mysqlclient
<中略＞
Installing collected packages: mysqlclient
Successfully installed mysqlclient-2.2.7
```
無事にインストールすることができました。
pipでモジュールを追加したので、忘れずに`requirements.txt`に追記します。

```shell:shell(appContainer)
(djangodev) root@xxxxxxxxxxxx:/usr/src/app# pip freeze > requirements.txt
```

これで無事にアプリケーションコンテナからデータベースコンテナに接続するための準備が整ったので、再度migrationのコマンドを実行します。

```shell:shell(appContainer)
(djangodev) root@xxxxxxxxxxxx:/usr/src/app# python manage.py migrate
<中略>
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying sessions.0001_initial... OK
```
OKとたくさん出ていれば成功していると思います。

このコマンドによりアプリケーションコンテナからデータベースコンテナに対して、テーブルが作成できているはずなので、データベースコンテナに入り、確認します。

```shell:shell(appContainer)
(djangodev) root@xxxxxxxxxxxx:/usr/src/app# exit
$ docker-compose exec db bash
bash-5.1#
bash-5.1# mysql -usample_user -psample_pass

<中略>

mysql>
mysql> use sample_db

<中略>

Database changed
mysql> show tables;
+----------------------------+
| Tables_in_sample_db        |
+----------------------------+
| auth_group                 |
| auth_group_permissions     |
| auth_permission            |
| auth_user                  |
| auth_user_groups           |
| auth_user_user_permissions |
| django_admin_log           |
| django_content_type        |
| django_migrations          |
| django_session             |
+----------------------------+
10 rows in set (0.01 sec)
```

上記のようにdjango関連のテーブルが作成されていれば、接続が成功しています。


### Dockerfileに必要な処理を追加
ところで、先程コンテナを再度作成した際に、OS関連のモジュールを追加するコマンド等を再度打たなければならない場面が発生したかと思います。
これを回避するために（再度作成した際に自動的にモジュールがインストールされるように）Dockerfileに下記のコマンドを追加しておきます。
```docker:djangotutorial/infra/app/Dockerfile
FROM python:3.12.8-slim-bookworm

# ここから追記！
# 必要モジュールのインストール
RUN apt-get update && apt-get install -y default-libmysqlclient-dev build-essential pkg-config

# 作業ディレクトリを設定
WORKDIR /usr/src/app

# requirements.txtをコピー
COPY requirements.txt /usr/src/app/

# 仮想環境作成 & pipでrequirements.txtをインストール
RUN python -m venv ~/.virtualenvs/djangodev && . ~/.virtualenvs/djangodev/bin/activate && pip install --upgrade pip && pip install --no-cache-dir -r requirements.txt
```

これで再作成してもモジュール等が最初からインストールされている状態になるかと思います。

## 最後に
今回はDjangoのチュートリアルに沿いながら、Dockerを用いてDjangoの開発環境を構築してみました。
Dockerを使うために公式にない部分の記載が結構多くなってしまいましたが、Dockerを使うことにより快適に開発を進めることができるようになりますため、是非実施していただけると幸いです。