Django + MySQLで開発をするときにやること

# はじめに
Djangoでwebアプリケーションの開発環境を作るときに毎度やるルーティンをいつでも見返せるように記します。[著: 大高隆 動かして学ぶ! Python Django 開発入門](https://www.amazon.co.jp/%E5%8B%95%E3%81%8B%E3%81%97%E3%81%A6%E5%AD%A6%E3%81%B6-Python-Django%E9%96%8B%E7%99%BA%E5%85%A5%E9%96%80-%E7%AC%AC2%E7%89%88-NEXT/dp/479817419X/ref=sr_1_8?crid=3RD6L2U1TBULU&keywords=django+%E6%9C%AC&qid=1668333095&qu=eyJxc2MiOiI0LjM1IiwicXNhIjoiNC4wNyIsInFzcCI6IjQuMDAifQ%3D%3D&sprefix=Django+%2Caps%2C1807&sr=8-8)を参考にしています。Djangoでアプリケーション開発を始めたい初心者向けです。

:::note warn
あくまでも素人が個人的に開発しているもののノートなので、セキュリティ上問題がある箇所があるかもしれません。自己責任でお願いします。
:::

# プロジェクトを作成する
```shell
django-admin startproject <プロジェクト名>
```
# settings.pyを編集する

## .envで環境変数を扱う
`settings.py`にある`SECRET_KEY`の文字列とmysql上でのユーザー名やパスワード等を`.env`ファイルという環境変数を扱うファイルに書きます。
```config
SECRET_KEY='django-insecure-0123456789qwertyuiop'

MYSQL_DATABASE='mysql'
MYSQL_USER='mysql'
MYSQL_PASSWORD='mysql'
MYSQL_HOST='mysql'
```

## 環境変数を読み込む
ここからが`settings.py`での作業になります。
`.env`に書いた環境変数を`settings.py`上で`env`に読み込ませます。
```python
import os, environ

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent

env = environ.Env()
env.read_env(os.path.join(BASE_DIR,'.env'))
```
秘密鍵を`env`で読み込ませます。これをしないとGithubに怒られます。
```python
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = env('SECRET_KEY')
```
`ALLOWED_HOSTS`を全て許可します。本番環境等では[注意](https://yu-nix.com/archives/django-allowed-hosts/)した方がいいかもしれません。

```python
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = ['*']
```
## データベースをMySQLに変更
`env`から環境変数を読み込ませて、パスワード等を安全に記述します。
```python
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': env('MYSQL_DATABASE'),  # データベース名
        'USER': env('MYSQL_USER'),
        'PASSWORD': env('MYSQL_PASSWORD'),
        'HOST': env('MYSQL_HOST'),
        'PORT': '3306',
        'OPTIONS': {
            'charset': 'utf8mb4',  # 文字コード
        },
    }
}
```
## 言語とタイムゾーンを日本仕様に変更
```python
LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'

USE_I18N = True

USE_TZ = True
```
## ロギングを設定
[著: 大高隆 動かして学ぶ! Python Django 開発入門](https://www.amazon.co.jp/%E5%8B%95%E3%81%8B%E3%81%97%E3%81%A6%E5%AD%A6%E3%81%B6-Python-Django%E9%96%8B%E7%99%BA%E5%85%A5%E9%96%80-%E7%AC%AC2%E7%89%88-NEXT/dp/479817419X/ref=sr_1_8?crid=3RD6L2U1TBULU&keywords=django+%E6%9C%AC&qid=1668333095&qu=eyJxc2MiOiI0LjM1IiwicXNhIjoiNC4wNyIsInFzcCI6IjQuMDAifQ%3D%3D&sprefix=Django+%2Caps%2C1807&sr=8-8)を引用
```python
# ロギング設定
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,

    'loggers': {
        'django': {
            'handlers': ['console'],
            'level': 'INFO',
        },
        'game': {
            'handlers': ['console'],
            'level': 'DEBUG',
        },
    },

    'handlers': {
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'dev',
        },
    },

    'formatters': {
        'dev': {
            'format': '\t'.join([
                '%(asctime)s',
                '[%(levelname)s]',
                '%(pathname)s(Line:%(lineno)d)',
                '%(message)s',
            ])
        },
    }
}
```
## 静的ファイル(static)が配置されている場所を設定
staticとは画像やcssです。javascriptとかもここに配置かも。
```python
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static'),
]
```
## セッション変数を設定
セッション変数を使うと別々のテンプレート(htmlファイル)やview間でも変数をやり取りすることができます。セッション変数は辞書型(連想配列)です。
```python
# セッションをキャッシュで保存する
SESSION_ENGINE = "django.contrib.sessions.backends.cache"

# 画面を閉じたらセッションが失効する
SESSION_EXPIRE_AT_BROWSER_CLOSE = True

SESSION_SAVE_EVERY_REQUEST = True
```
# docker-composeで環境構築
開発メンバーが簡単に環境構築できるようにするため、docker-composeで環境構築をします。
## Dockerfileの作成
[これ](https://docs.docker.jp/compose/django.html)をそのまま引用してきました。
```Dockerfile
FROM python:3
ENV PYTHONUNBUFFERED 1
RUN mkdir /code
WORKDIR /code
ADD requirements.txt /code/
RUN pip install -r requirements.txt
ADD . /code/
```
## docker-compose.ymlの作成
Django + MySQLの環境を作ります。文字コードを`utf8mb4`に command や volumes を記述していますが、不要な方は適宜削除してください。また、`.env`ファイルで記述した環境変数をこちらでも`${MYSQL_USER}`のように読み込んでいます。
```yaml
version: "3"

services:
  db:
    image: mysql
    restart: always
    hostname: mysql
    environment:
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
      MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
    # データベースの文字コードを utf8mb4 に変更
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    volumes:
      - ./my.cnf:/etc/my.cnf

  web:
    build: .
    command: python3 manage.py runserver 0.0.0.0:8000
    volumes:
      - .:/code
    ports:
      - "8000:8000"
    depends_on:
      - db
```
## requirements.txtの作成
必要な任意のライブラリを記述しましょう。
```text
Django
mysqlclient
django-environ
requests
numpy
matplotlib
environ
pandas
```

## my.cnfの作成 (任意)
もしMySQLでの文字コードに不具合がある方はこのように`my.cnf`を編集・作成してみてください。
```config
[mysqld]
character-set-server=utf8mb4

skip-host-cache
skip-name-resolve
datadir=/var/lib/mysql
socket=/var/run/mysqld/mysqld.sock
secure-file-priv=/var/lib/mysql-files
user=mysql

pid-file=/var/run/mysqld/mysqld.pid
[client]
default-character-set=utf8mb4
socket=/var/run/mysqld/mysqld.sock

!includedir /etc/mysql/conf.d/
```
# アプリケーションを追加する
アプリケーションを以下のように追加した後にやることを記します。今回は`game`というアプリケーション名という体で進めます。
```
python manage.py startapp <アプリケーション名>
```
## settingsにアプリケーションを追加する
`<プロジェクト名>/settings.py`に追加したアプリケーションを記述します。
```python
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'game.apps.GameConfig',  # こちらを追加
]
```
## ルーティングを設定
まずは`<プロジェクト名>`下にある`urls.py`に以下のように記述する。
```python
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('game.urls'))  # 追加したアプリケーションを記述
]
```
次に、`game(アプリケーション名)`下に`urls.py`を作成し、以下のように記述する。
```python
from django.urls import path

from . import views


app_name = 'game'
urlpatterns = [
    path('', views.IndexView.as_view(), name="index")
]
```

## ビューを作成
`views.py`にはサーバー側(?)でやりたい操作を記述します。ここが脳みたいなところと思っています。
```python
from django.views import generic


class IndexView(generic.TemplateView):
    template_name = "index.html"
```
## テンプレートを作成
`game(アプリケーション名)`下に`templates`というディレクトリを作成し、そこにテンプレート(htmlファイル)を置いていく。
```html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>トップページ</title>
</head>

<body>
    hello world!
<body
</html>
```

# docker-composeを動かしてみる
まずbuildします。
```shell
docker-compose build
```
次にupします。
```shell
docker-compose up
```
もしうまくいかない場合はデータベース側のコンテナを先に立ち上げてからweb側のコンテナを立ち上げると良いかもです。
```
docker-compose up -d db
docker-compose up
```
うまくいくとターミナル上に`http://0.0.0.0:8000/`が現れると思うので、それをブラウザに貼り付けてください。ブラウザに貼り付けた後にダメだった人は`http://localhost:8000/`でうまくいけるかもしれません。ブラウザ上に**hello world**と出ていれば成功です。

# おわりに
私は就活中(2022/11/13)ですが、webアプリケーション開発の経験が少しでもあると、インターンの選考等でかなり有利になると思いました。お時間のある時にぜひ、始めてみてください。