尝试将 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 具有 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
这是否意味着 unicode char 在 PostgreSQL 中有 4 个字节的 maxlen 但在 MySQL 中有 3 个字节导致上述错误?
这些答案都没有为我解决问题。根本原因是:
您不能使用 utf-8 字符集在 MySQL 中存储 4 字节字符。
MySQL 有一个 3 byte limit on utf-8 characters(是的,它很古怪,nicely summed up by a Django developer here)
要解决此问题,您需要:
更改您的 MySQL 数据库、表和列以使用 utf8mb4 字符集(仅从 MySQL 5.5 开始可用)在 Django 设置文件中指定字符集,如下所示:
设置.py
DATABASES = {
'default': {
'ENGINE':'django.db.backends.mysql',
...
'OPTIONS': {'charset': 'utf8mb4'},
}
}
注意:重新创建数据库时,您可能会遇到“Specified key was too long”问题。
最可能的原因是一个 CharField
,它的 max_length 为 255,并且上面有某种索引(例如,唯一的)。因为 utf8mb4 使用的空间比 utf-8 多 33%,所以您需要将这些字段缩小 33%。
在这种情况下,将 max_length 从 255 更改为 191。
或者,您可以edit your MySQL configuration to remove this restriction 但不能没有一些 django 黑客
更新:我又遇到了这个问题,结果是 switching to PostgreSQL,因为我无法将 VARCHAR
减少到 191 个字符。
我有同样的问题,并通过更改列的字符集解决了它。即使您的数据库具有默认字符集 utf-8
,我认为数据库列在 MySQL 中也可能具有不同的字符集。这是我使用的 SQL QUERY:
ALTER TABLE database.table MODIFY COLUMN col VARCHAR(255)
CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
如果你有这个问题,这里有一个 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()
db.close()
之前添加 db.commit()
之前,此代码对我不起作用。
如果它是一个新项目,我会删除数据库,并使用适当的字符集创建一个新项目:
CREATE DATABASE <dbname> CHARACTER SET utf8;
- --character-set-server=utf8
--character-set-server=utf8mb4
我只是想出了一种避免上述错误的方法。
保存到数据库
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 表中并在渲染到模板进行显示之前对其进行解码的唯一方法吗?
.encode('unicode_escape')
时,您实际上并没有在数据库中存储 unicode 字符。您强制所有客户端在使用它们之前取消编码,这意味着它无法与 django.admin 或其他各种东西一起正常工作。
utf8
字符集中保存 4 字节 UTF-8 字符(如表情符号)的少数方法之一。
utf8mb4
的编码允许存储比基本多语言平面更多的内容。我知道,你会认为“UTF8”是完全存储 Unicode 所需要的。好吧,whaddaya 知道,它不是。请参阅dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html
您可以将文本字段的排序规则更改为 UTF8_general_ci,问题将得到解决。
请注意,这不能在 Django 中完成。
改进@madprops 答案 - 作为 django 管理命令的解决方案:
import MySQLdb
from django.conf import settings
from django.core.management.base import BaseCommand
class Command(BaseCommand):
def handle(self, *args, **options):
host = settings.DATABASES['default']['HOST']
password = settings.DATABASES['default']['PASSWORD']
user = settings.DATABASES['default']['USER']
dbname = settings.DATABASES['default']['NAME']
db = MySQLdb.connect(host=host, user=user, passwd=password, 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:
print(f'Changing table "{row[0]}"...')
sql = "ALTER TABLE `%s` convert to character set DEFAULT COLLATE DEFAULT" % (row[0])
cursor.execute(sql)
db.close()
希望这对我以外的任何人都有帮助:)
sql = "ALTER TABLE `%s` convert to character set DEFAULT COLLATE DEFAULT" % (row[0])
应更改为 sql = "ALTER TABLE `%s` convert to character set 'utf8' COLLATE 'utf8_unicode_ci' " % (row[0])
。谢谢你的最佳答案。
您不是在尝试保存 unicode 字符串,而是在尝试以 UTF-8 编码保存字节字符串。使它们成为实际的 unicode 字符串文字:
user.last_name = u'Slatkevičius'
或(当您没有字符串文字时)使用 utf-8 编码对其进行解码:
user.last_name = lastname.decode('utf-8')
只需改变你的桌子,不需要任何东西。只需在数据库上运行此查询。 ALTER TABLE table_name
转换为字符集 utf8
它肯定会起作用。
不定期副业成功案例分享
'charset': 'utf8mb4'
选项也很重要。最后,索引问题是一团糟。删除列上的索引,或使其长度不超过 191,或使用TextField
代替!mysql.connector.django
作为数据库后端,您还必须在OPTIONS
中设置'collation': 'utf8mb4_unicode_ci'
。