在Django中保存unicodestring时,MySQL“错误的string值”错误
尝试将first_name,last_name保存到Django的auth_user模型时,出现奇怪的错误消息。
失败的例子
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具有UTF8_general_cisorting规则的utf-8字符集。
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
这是否意味着unicode char在PostgreSQL中有4个字节的maxlen,但是在MySQL中有3个字节导致了上述错误?
我有同样的问题,并通过更改列的字符集来解决它。 即使你的数据库有一个默认的utf-8
字符集,我认为数据库列在MySQL中可能有不同的字符集。 以下是我使用的SQL QUERY:
ALTER TABLE database.table MODIFY COLUMN col VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
这些答案都没有解决我的问题。 根本原因是:
您不能在UTF-8字符集中的MySQL中存储4字节字符。
MySQL 在utf-8字符上有3个字节的限制 (是的,它是怪人, 很好的由Django开发者在这里总结 )
要解决这个问题,你需要:
- 更改您的MySQL数据库,表和列以使用utf8mb4字符集 (仅适用于MySQL 5.5以上版本)
- 在您的Django设置文件中指定字符集,如下所示:
settings.py
DATABASES = { 'default': { 'ENGINE':'django.db.backends.mysql', ... 'OPTIONS': {'charset': 'utf8mb4'}, } }
注意:重新创build数据库时,可能会遇到“ 指定密钥太长 ”的问题。
最可能的原因是一个CharField
,它有一个max_length为255,并在其上的某种索引(如唯一)。 由于utf8mb4使用的空间比utf-8多33%,因此您需要将这些字段缩小33%。
在这种情况下,请将max_length从255更改为191。
或者,你可以编辑你的MySQLconfiguration,以消除这个限制, 但不是没有一些Django hackery
更新:我刚刚遇到了这个问题,并最终切换到PostgreSQL,因为我无法减less我的VARCHAR
191个字符。
如果你有这个问题,这是一个Python脚本自动更改你的MySQL数据库的所有列。
#! /usr/bin/env python import MySQLdb host = "localhost" passwd = "passwd" user = "youruser" dbname = "yourdbname" db = MySQLdb.connect(host=host, user=user, passwd=passwd, db=dbname) cursor = db.cursor() cursor.execute("ALTER DATABASE `%s` CHARACTER SET 'utf8' COLLATE 'utf8_unicode_ci'" % dbname) sql = "SELECT DISTINCT(table_name) FROM information_schema.columns WHERE table_schema = '%s'" % dbname cursor.execute(sql) results = cursor.fetchall() for row in results: sql = "ALTER TABLE `%s` convert to character set DEFAULT COLLATE DEFAULT" % (row[0]) cursor.execute(sql) db.close()
如果这是一个新项目,我只需要删除数据库,然后用适当的字符集创build一个新的项目:
CREATE DATABASE <dbname> CHARACTER SET utf8;
我只是想出了一种避免上述错误的方法。
保存到数据库
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
这是唯一的方法来保存这样的string到MySQL表并解码它之前呈现模板显示?
您可以将文本字段的sorting规则更改为UTF8_general_ci,问题将得到解决。
注意,这不能在Django中完成。
你不是想保存unicodestring,而是试图以UTF-8编码保存字节串。 让他们真正的Unicodestring文字:
user.last_name = u'Slatkevičius'
或者(当你没有string文字时)使用utf-8编码对它们进行解码:
user.last_name = lastname.decode('utf-8')