データベース名を変更したらマイグレーションエラー MySQLdb._exceptions.DataError (1406, Data too long for column 'name') が発生したときの対処法

背景

Djangoでモデル実装をして、マイグレーションを実行しました。
その後、テータベース名が適切ではないという話になり、データベース名を変更することになりました。

其の際に思わぬエラーが発生したので記しておきます。

設定ファイルのデータベース名の変更

設定ファイル内でデータベース名の記載があったのは二つでした。

  • docker-compose.yml
  • front/settings.py

それぞれ新しいデータベース名に変更します。(frontはアプリケーション名)

新規データベースの作成

データベースのrenameができないということと、個々のテーブルを別データベースの下にrenameするのは数が多くて大変だったので、新規でデータベースを作成し、権限を付与した後にマイグレーションすることにしました。

データベースコンテナを起動した状態で、アプリケーションコンテナにログインして、mysqlコマンドでデータベースに接続しました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
$ docker-compose run --rm web /bin/bash
Creating example_web_run ... done
root@ddad35e91809:/code# mysql -u root -p -h db
Enter password:
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MySQL connection id is 30
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)]> create database new_db;
Query OK, 1 row affected (0.002 sec)

MySQL [(none)]> grant all privileges on `new_db`.* to 'django'@'%';
Query OK, 0 rows affected (0.002 sec)

MySQL [(none)]> grant all privileges on `test_new_db`.* to 'django'@'%';
Query OK, 0 rows affected (0.002 sec)

MySQL [(none)]> exit
Bye
root@ddad35e91809:/code#
root@ddad35e91809:/code# exit
exit
$

マイグレーションでエラー発生

これでマイグレーションすれば大丈夫と思っていて、マイグレーションを実行しました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
$ docker-compose run --rm web python manage.py migrate front
Creating example_web_run ... done
Operations to perform:
Apply all migrations: front
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying front.0001_initial... OK
Traceback (most recent call last):
File "/usr/local/lib/python3.10/site-packages/django/db/backends/utils.py", line 85, in _execute
return self.cursor.execute(sql, params)
File "/usr/local/lib/python3.10/site-packages/django/db/backends/mysql/base.py", line 73, in execute
return self.cursor.execute(query, args)
File "/usr/local/lib/python3.10/site-packages/MySQLdb/cursors.py", line 206, in execute
res = self._query(query)
File "/usr/local/lib/python3.10/site-packages/MySQLdb/cursors.py", line 319, in _query
db.query(q)
File "/usr/local/lib/python3.10/site-packages/MySQLdb/connections.py", line 254, in query
_mysql.connection.query(self, query)
MySQLdb._exceptions.DataError: (1406, "Data too long for column 'name' at row 33")

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File "/code/manage.py", line 22, in <module>
main()
File "/code/manage.py", line 18, in main
execute_from_command_line(sys.argv)
File "/usr/local/lib/python3.10/site-packages/django/core/management/__init__.py", line 425, in execute_from_command_line
utility.execute()
File "/usr/local/lib/python3.10/site-packages/django/core/management/__init__.py", line 419, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/usr/local/lib/python3.10/site-packages/django/core/management/base.py", line 373, in run_from_argv
self.execute(*args, **cmd_options)
File "/usr/local/lib/python3.10/site-packages/django/core/management/base.py", line 417, in execute
output = self.handle(*args, **options)
File "/usr/local/lib/python3.10/site-packages/django/core/management/base.py", line 90, in wrapped
res = handle_func(*args, **kwargs)
File "/usr/local/lib/python3.10/site-packages/django/core/management/commands/migrate.py", line 277, in handle
emit_post_migrate_signal(
File "/usr/local/lib/python3.10/site-packages/django/core/management/sql.py", line 46, in emit_post_migrate_signal
models.signals.post_migrate.send(
File "/usr/local/lib/python3.10/site-packages/django/dispatch/dispatcher.py", line 170, in send
return [
File "/usr/local/lib/python3.10/site-packages/django/dispatch/dispatcher.py", line 171, in <listcomp>
(receiver, receiver(signal=self, sender=sender, **named))
File "/usr/local/lib/python3.10/site-packages/django/contrib/auth/management/__init__.py", line 83, in create_permissions
Permission.objects.using(using).bulk_create(perms)
File "/usr/local/lib/python3.10/site-packages/django/db/models/query.py", line 519, in bulk_create
returned_columns = self._batched_insert(
File "/usr/local/lib/python3.10/site-packages/django/db/models/query.py", line 1324, in _batched_insert
self._insert(item, fields=fields, using=self.db, ignore_conflicts=ignore_conflicts)
File "/usr/local/lib/python3.10/site-packages/django/db/models/query.py", line 1301, in _insert
return query.get_compiler(using=using).execute_sql(returning_fields)
File "/usr/local/lib/python3.10/site-packages/django/db/models/sql/compiler.py", line 1441, in execute_sql
cursor.execute(sql, params)
File "/usr/local/lib/python3.10/site-packages/django/db/backends/utils.py", line 99, in execute
return super().execute(sql, params)
File "/usr/local/lib/python3.10/site-packages/django/db/backends/utils.py", line 67, in execute
return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
File "/usr/local/lib/python3.10/site-packages/django/db/backends/utils.py", line 76, in _execute_with_wrappers
return executor(sql, params, many, context)
File "/usr/local/lib/python3.10/site-packages/django/db/backends/utils.py", line 80, in _execute
with self.db.wrap_database_errors:
File "/usr/local/lib/python3.10/site-packages/django/db/utils.py", line 90, in __exit__
raise dj_exc_value.with_traceback(traceback) from exc_value
File "/usr/local/lib/python3.10/site-packages/django/db/backends/utils.py", line 85, in _execute
return self.cursor.execute(sql, params)
File "/usr/local/lib/python3.10/site-packages/django/db/backends/mysql/base.py", line 73, in execute
return self.cursor.execute(query, args)
File "/usr/local/lib/python3.10/site-packages/MySQLdb/cursors.py", line 206, in execute
res = self._query(query)
File "/usr/local/lib/python3.10/site-packages/MySQLdb/cursors.py", line 319, in _query
db.query(q)
File "/usr/local/lib/python3.10/site-packages/MySQLdb/connections.py", line 254, in query
_mysql.connection.query(self, query)
django.db.utils.DataError: (1406, "Data too long for column 'name' at row 33")
ERROR: 1

長々とエラーメッセージが出力されましたが、ポイントは

1
django.db.utils.DataError: (1406, "Data too long for column 'name' at row 33")

と思いました

状況確認

エラーメッセージで調べても、データが溢れてるんじゃない?とか、データ溢れた場合にエラーが発生しないようにするにはsql_mode=’’にすればいいよという内容しか出てきませんでした。

nameカラムの確認

nameというカラムを持つテーブルが複数あり、max_lengthなどを確認しましたが、だいたい128が設定されていました。そもそもマイグレーションではデータを入れることはないので、調べる箇所が検討違いでした。

マイグレーションの状況

テーブルが正常に作成されているかどうかを確認したのですが、正常に作成されていました。

テスト

変えたのはDB名のみです。なので、データベース名の長さが問題なのかと思い、長いデータベース名や短いデータベース名を試してみましたが、どの場合でもエラーが発生しました。ただ唯一、以前のデータベース名のみエラーが発生しないという状況でした。

解決方法

一旦全てのテーブルをロールバックしてから行ってみようと思い、アプリケーションすべてをロールバックしました。

その後、マイグレーションを行うとエラーは発生せず、マイグレーションも行われていました。

エラーメッセージからnameというカラムに問題があるのかと思ったのですが、一旦全てきれいにしてからマイグレーションを行えばデータベース名の変更は問題なさそうです。

ですが、もともとデータベースは新しく作成しているので、各アプリケーションのテーブルが影響しているとは考えづらいですね…

まとめ

データベース名の変更は極力行わない。行う場合は全てのアプリケーションのテーブルをロールバックしてから再度マイグレーションを行う。

参考図書