1. ホーム
  2. python

[解決済み] Djangoでユニコード文字列を保存すると、MySQLの「不正な文字列値」エラーが発生する。

2022-04-22 22:18:18

質問

Django の auth_user モデルに first_name, last_name を保存しようとすると、変なエラーメッセージが表示されます。

失敗例

user = User.object.create_user(username, email, password)
user.first_name = u'Rytis'
user.last_name = u'Slatkevičius'
user.save()
>>> Incorrect string value: '\xC4\x8Dius' for column 'last_name' at row 104

user.first_name = u'Валерий'
user.last_name = u'Богданов'
user.save()
>>> Incorrect string value: '\xD0\x92\xD0\xB0\xD0\xBB...' for column 'first_name' at row 104

user.first_name = u'Krzysztof'
user.last_name = u'Szukiełojć'
user.save()
>>> Incorrect string value: '\xC5\x82oj\xC4\x87' for column 'last_name' at row 104

サクシードの例

user.first_name = u'Marcin'
user.last_name = u'Król'
user.save()
>>> SUCCEED

MySQLの設定

mysql> show variables like 'char%';
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | utf8                       | 
| character_set_connection | utf8                       | 
| character_set_database   | utf8                       | 
| character_set_filesystem | binary                     | 
| character_set_results    | utf8                       | 
| character_set_server     | utf8                       | 
| character_set_system     | utf8                       | 
| character_sets_dir       | /usr/share/mysql/charsets/ | 
+--------------------------+----------------------------+
8 rows in set (0.00 sec)

テーブルの文字セットと照合順序

テーブル auth_user は utf-8 文字セットと utf8_general_ci 照合順序を持っています。

UPDATEコマンドの結果

UPDATEコマンドでauth_userテーブルに上記の値を更新しても、エラーは発生しませんでした。

mysql> update auth_user set last_name='Slatkevičiusa' where id=1;
Query OK, 1 row affected, 1 warning (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select last_name from auth_user where id=100;
+---------------+
| last_name     |
+---------------+
| Slatkevi?iusa | 
+---------------+
1 row in set (0.00 sec)

PostgreSQL

Djangoのデータベースバックエンドを切り替えたところ、上記の失敗した値がPostgreSQLのテーブルに更新されるようになりました。不思議です。

mysql> SHOW CHARACTER SET;
+----------+-----------------------------+---------------------+--------+
| Charset  | Description                 | Default collation   | Maxlen |
+----------+-----------------------------+---------------------+--------+
...
| utf8     | UTF-8 Unicode               | utf8_general_ci     |      3 | 
...

しかし http://www.postgresql.org/docs/8.1/interactive/multibyte.html , 以下のようなことがわかりました。

Name Bytes/Char
UTF8 1-4

PostgreSQLではunicode charのmaxlenは4バイトですが、MySQLでは3バイトなので上記のエラーが発生したということでしょうか?

どのように解決するのですか?

上記のエラーを回避する方法を1つだけ思いつきました。

データベースへの保存

user.first_name = u'Rytis'.encode('unicode_escape')
user.last_name = u'Slatkevičius'.encode('unicode_escape')
user.save()
>>> SUCCEED

print user.last_name
>>> Slatkevi\u010dius
print user.last_name.decode('unicode_escape')
>>> Slatkevičius

このような文字列をMySQLのテーブルに保存し、それをデコードしてからテンプレートにレンダリングして表示する方法はこれしかないのでしょうか?