Djangoでテスト実行時にテスト用データベース作成エラーが発生した時の対応

背景

以前の記事でdocker-composeを利用してDjango+MySQLの環境を作成しました。

プロジェクトとアプリケーションを作成し、テストを実行するところで、以下のエラーが発生しました。

1
2
3
4
$ docker-compose run --rm web python manage.py test
Creating test database for alias 'default'...
Got an error creating the test database: (1044, "Access denied for user 'django'@'%' to database 'test_example'")
ERROR: 2

初めてのDjangoでのテスト実行ということもあってわからないことが多かったので調べた結果を記載していきます。

テスト用データベースについて

エラーメッセージを見て思ったのは、どのようにしてテスト用データベースの名前が決まっているのか?ということでした。公式ドキュメントを探していると見つかりました。

テストを書いて実行する | Django ドキュメント | Django

こちらによると、

テストデータベースのデフォルトの名前は、 DATABASES 設定内の各 NAME の値の前に test_ を付けたものになります。設定の DATABASES 内の TEST ディクショナリには、テストデータベースに対するいろいろな設定を書くことができます。例えば、別のデータベース名を指定したければ、 TEST ディクショナリの NAME に、 DATABASES の中から好きなデータベースを選んで指定することができます。

ということでした。TESTディクショナリを指定していないので、結果としては、test_exampleで良さそうです。

settings.pyは以下のようになっています。

1
2
3
4
5
6
7
8
9
10
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'example',
'USER': 'django',
'PASSWORD': 'django',
'HOST': 'db',
'PORT': 3306,
}
}

TESTディクショナリを設定する場合は

1
2
3
4
5
6
7
8
9
10
11
12
13
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'example',
'USER': 'django',
'PASSWORD': 'django',
'HOST': 'db',
'PORT': 3306,
'TEST': {
'NAME': 'custom_test_database'
}
}
}

となります。

現状の確認

データベースの確認

データベースが存在していないのか、存在しているがアクセスできないのかを確認したいと思います。まずはdbshellでアクセスしてみます。

1
2
3
4
5
6
7
8
9
$ docker-compose run --rm web python manage.py dbshell
MySQL[example]> show databases;
+---------------------+
| Database |
+---------------------+
| information_schema |
| example |
+---------------------+
2 rows in set (0.001 sec)

データベースがないように見えます。権限の問題かもしれないので権限を確認します。

権限の確認

次に権限を確認します。権限の確認にはshow grants forというSQL文があるので実行します。

1
2
3
4
5
6
7
8
MySQL[example]> show grants for 'django'@'%';
+-----------------------------------------------------+
| Grants for django@% |
+-----------------------------------------------------+
| GRANT USAGE ON *.* TO 'django'@'%' |
| GRANT ALL PRIVILEGES ON `example`.* TO 'django'@'%' |
+-----------------------------------------------------+
2 rows in set (0.001 sec)

GRANT USAGEは見慣れない権限なのですが、権限を与えないということのようです。全ての権限をなくしてからexampleデータベースにのみ全権限を与えているということのようです。

これでは現状がわからないのでrootで接続してみます。

webコンテナにログインしてからmysqlクライアントで接続します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ docker-compose run --rm web /bin/bash 
Creating example_web_run ... done
root@6e114e4f73b3:/code#
root@6e114e4f73b3:/code# mysql -u root -p -h db
Enter password:
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 5.7.36 MySQL Community Server (GPL)

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MySQL [(none)]>

rootのパスワードはdocker-compose.ymlでMYSQL_ROOT_PASSWORD環境変数で指定したパスワードを入力しました。

ではデータベースを確認します。

1
2
3
4
5
6
7
8
9
10
11
12
13
MySQL [(none)]> show databases;
+---------------------+
| Database |
+---------------------+
| information_schema |
| example |
| mysql |
| performance_schema |
| sys |
+---------------------+
5 rows in set (0.010 sec)

MySQL [(none)]>

データベースはありませんでした。一旦作成してみます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
MySQL [(none)]> create database test_example;
Query OK, 1 row affected (0.003 sec)

MySQL [(none)]> show databases;
+--------------------------+
| Database |
+--------------------------+
| example |
| information_schema |
| mysql |
| performance_schema |
| sys |
| test_example |
+--------------------------+
6 rows in set (0.001 sec)

権限も付与します。

1
2
3
4
5
6
7
8
9
10
11
12
13
MySQL [(none)]> grant all privileges on `test_example`.* to 'django'@'%';
Query OK, 0 rows affected (0.003 sec)

MySQL [(none)]>
MySQL [(none)]> show grants for 'django'@'%';
+----------------------------------------------------------------------+
| Grants for django@% |
+----------------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'django'@'%' |
| GRANT ALL PRIVILEGES ON `example`.* TO 'django'@'%' |
| GRANT ALL PRIVILEGES ON `test_example`.* TO 'django'@'%' |
+----------------------------------------------------------------------+
3 rows in set (0.001 sec)

権限の付与ができました。

テスト再実行

ではテストを再実行してみます。

1
2
3
4
5
6
7
8
9
10
11
12
13
$ docker-compose run --rm web python manage.py test
Creating example_web_run ... done
Creating test database for alias 'default'...
Got an error creating the test database: (1007, "Can't create database 'test_example'; database exists")
Type 'yes' if you would like to try deleting the test database 'test_example', or 'no' to cancel: yes
Destroying old test database for alias 'default'...
System check identified no issues (0 silenced).
..
----------------------------------------------------------------------
Ran 2 tests in 0.006s

OK
Destroying test database for alias 'default'...

すでにデータベースがあるので削除していいかと聞かれてしまいました…
yesとタイプして先に進むと、無事テストが通りました。

もう一度実行してみます。

1
2
3
4
5
6
7
8
9
10
$ docker-compose run --rm web python manage.py test
Creating example_web_run ... done
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
..
----------------------------------------------------------------------
Ran 2 tests in 0.010s

OK
Destroying test database for alias 'default'...

今度は問題なくできました。

ということは、権限のみ与えれば良いということになります。

まとめ

Djangoのテストを実行するために、接続ユーザーに対してテスト用データベースへの権限を付与する必要があるということがわかりました。

新しい環境を作成したあとの初回は都度権限を付与するのはちょっと面倒な印象があります。MySQLコンテナでの/docker-entrypoint-initdb.dを利用した初期化がうまく使えないか検証してみようと思います。