在Django中保存unicode字符串时,MySQL"错误的字符串值"错误

jac*_*ack 150 python mysql django unicode utf-8

尝试将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
Run Code Online (Sandbox Code Playgroud)

成功的例子

user.first_name = u'Marcin'
user.last_name = u'Król'
user.save()
>>> SUCCEED
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

表charset和整理

表auth_user具有带utf8_general_ci排序规则的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)
Run Code Online (Sandbox Code Playgroud)

PostgreSQL的

当我在Django中切换数据库后端时,上面列出的失败值可以更新到PostgreSQL表中.真奇怪.

mysql> SHOW CHARACTER SET;
+----------+-----------------------------+---------------------+--------+
| Charset  | Description                 | Default collation   | Maxlen |
+----------+-----------------------------+---------------------+--------+
...
| utf8     | UTF-8 Unicode               | utf8_general_ci     |      3 | 
...
Run Code Online (Sandbox Code Playgroud)

但是从http://www.postgresql.org/docs/8.1/interactive/multibyte.html,我发现了以下内容:

Name Bytes/Char
UTF8 1-4
Run Code Online (Sandbox Code Playgroud)

这是否意味着unicode char在PostgreSQL中有4个字节的maxlen但在MySQL中有3个字节导致上述错误?

don*_*ner 120

这些答案都没有解决我的问题.根本原因是:

您不能使用utf-8字符集在MySQL中存储4字节字符.

MySQL 对utf-8字符3个字节的限制(是的,它是wack,很好地由Django开发人员在这里总结)

要解决这个问题,您需要:

  1. 更改MySQL数据库,表和列以使用utf8mb4字符集(仅从MySQL 5.5开始提供)
  2. 在Django设置文件中指定charset,如下所示:

settings.py

DATABASES = {
    'default': {
        'ENGINE':'django.db.backends.mysql',
        ...
        'OPTIONS': {'charset': 'utf8mb4'},
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:重新创建数据库时,可能会遇到" 指定密钥太长 "问题.

最可能的原因是CharFieldmax_length为255且其上有某种索引(例如唯一).因为utf8mb4比utf-8多占用33%的空间,所以你需要将这些字段缩小33%.

在这种情况下,将max_length从255更改为191.

或者,您可以编辑MySQL配置以删除此限制, 但不能没有一些django hackery

更新:我刚刚再次遇到这个问题并最终切换到PostgreSQL,因为我无法将我减少VARCHAR到191个字符.

  • 这个答案需要方式,方式,方式更多的赞成.谢谢!真正的问题是你的应用程序可能会运行多年,直到有人试图输入一个4字节的字符. (11认同)
  • 这绝对是正确的答案.OPTIONS设置对于使django解码表情符号并将其存储在MySQL中至关重要.只是通过SQL命令将mysql charset更改为utf8mb4是不够的! (2认同)
  • 我喜欢你的[链接](https://code.djangoproject.com/ticket/18392#comment:16)这句话:_这只是MySQL被故意且不可逆转地大脑损伤的另一个例子。_:) (2认同)

use*_*478 117

我遇到了同样的问题,并通过更改列的字符集来解决它.即使您的数据库具有默认字符集,utf-8我认为数据库列可能在MySQL中具有不同的字符集.这是我使用的SQL QUERY:

    ALTER TABLE database.table MODIFY COLUMN col VARCHAR(255)
    CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
Run Code Online (Sandbox Code Playgroud)

  • 呃,我改变了所有可能的字符集,直到我真正重新阅读这个答案:*columns*可以拥有自己的*字符集,独立于表和数据库.这很疯狂,也是我的问题. (14认同)

mad*_*ops 69

如果你有这个问题,这里是一个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()
Run Code Online (Sandbox Code Playgroud)

  • 这个解决方案使用django应用程序解决了我的所有问题,该应用程序存储文件和目录路径.将dbname作为django数据库抛出并让它运行.像魅力一样工作! (4认同)

Van*_*uan 21

如果它是一个新项目,我只是删除数据库,并创建一个具有适当字符集的新数据库:

CREATE DATABASE <dbname> CHARACTER SET utf8;
Run Code Online (Sandbox Code Playgroud)


jac*_*ack 9

我只想出一种避免上述错误的方法.

保存到数据库

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
Run Code Online (Sandbox Code Playgroud)

这是将这样的字符串保存到MySQL表中并在渲染到模板以进行显示之前对其进行解码的唯一方法吗?

  • 我遇到了类似的问题,但我不同意这是一个有效的解决方案.当你`.encode('unicode_escape')`你实际上并没有在数据库中存储unicode字符.在使用它们之前,你强迫所有客户端取消编码,这意味着它无法与django.admin或其他各种东西一起正常工作. (12认同)
  • 虽然存储转义码而不是字符似乎令人讨厌,但这可能是在MySQL 5.1的3字节`utf8`字符集中保存4字节UTF-8字符(如表情符号)的几种方法之一. (3认同)
  • 有一个名为`utf8mb4`的编码允许存储多个基本多语言平面.我知道,你认为"UTF8"就是完全存储Unicode所需要的.嗯,whaddaya知道,事实并非如此.见http://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html (2认同)

Wei*_* An 6

您可以将文本字段的排序规则更改为UTF8_general_ci,问题将得到解决.

注意,这不能在Django中完成.