Django + MySQLをDockerで動かしたい ~tips編~
DockerでDjango + MySQLの構成で開発をしたときに困ったあれこれをメモとして残していきます。
環境構築はたくさん記事があると思うので、環境構築の手順はそちらに託すことにします。
DBにデータが登録できない(文字コードが違う)
adminからデータ登録をしようとした時に遭遇したエラーです。
エラー文と問題詳細
(1267, "Illegal mix of collations (latin1_swedish_ci,IMPLICIT) and (utf8_general_ci,COERCIBLE) for operation '='")
ざっくり言うと、文字コードが違うのでデータ登録ができないと怒られています。
MySQLに入って
mysql> SHOW VARIABLES WHERE Variable_name LIKE 'character\_set\_%' OR Variable_name LIKE 'collation%';
を実行すると、character_set_*
に latin1
、collation_*
に latin1_sweden_ci
が入ってると思います。
シングルバイトの文字コードが指定されているところに、漢字などマルチバイトのデータを登録しようとしてエラーが出ています。
解決方法
クライアント側(Django)とサーバ側(MySQL)とでそれぞれ設定が必要です。
クライアント側(Django)の設定
app/settings.py
のDBの設定欄にOPTIONSで文字コードを指定します。
# Database # https://docs.djangoproject.com/en/4.1/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'django', 'USER': 'root', 'PASSWORD': '', 'HOST': 'db', 'PORT': 3306, 'OPTIONS': { 'charset': 'utf8mb4' } } }
サーバ側の設定
docker runの時にオプションを指定する方法もあるのですが、今回はdocker-compose.yamlにcommand
として指定します。
db: image: mysql:5.7 command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci container_name: mysql volumes: - ./database/data:/var/lib/mysql ports: - 3333:3306 environment: MYSQL_DATABASE: 'django' MYSQL_ALLOW_EMPTY_PASSWORD: 'true' platform: linux/amd64
これでOKです!
あとはdockerを立ち上げ直せばうまくいくはずです!
Models.pyでFunctionKeyを使っているとうまく変更できない場合があります。
その時は、FunctionKeyを使っているモデル(テーブル)をコメントアウトして、
root@123456:/django# python manage.py makemigrations root@123456:/django# python manage.py migrate
をして設定変更を適用した後にコメントアウトを解除してまたmigrateするとうまくいきます。
マスタデータ・テストデータを共有したい(seedしたい)
adminからデータ登録もできるのですが、一括登録したい場合面倒です。
テストデータの共有ができればチーム開発も楽になりますね。
TLDR
app/fixures
配下にテストデータ用のjsonを作って、次のコマンドでポストします。
python manage.py loaddata app/fixtures/test_data.json
データが入っていることは、mysqlを起動してSELECT文を実行するか、Djangoのadmin画面から確認できます。
Models.pyにテーブル定義
app/models.py
に下記のテーブルを定義したとします。
from django.db import models class User(models.Model): GENDER = ( (1, 'male'), (2, 'female') ) family_name = models.CharField(max_length=50) first_name = models.CharField(max_length=50) gender = models.IntegerField(choices=GENDER) birthday = models.DateField() def __str__(self): return f'{self.id}: {self.family_name} {self.first_name}'
test_data.jsonを作成
テストデータ投入用のJSONファイルを作成します。
[ { "model": "app.user", "pk": 1, "fields": { "familiy_name" : "田中", "first_name": "太郎", "gender": 1, "birthday": 1995-01-01 } }, { "model": "app.user", "pk": 2, "fields": { "familiy_name" : "後藤", "first_name": "花子", "gender": 2, "birthday": 1975-10-26 } } ]