ChatGPT解决这个技术问题 Extra ChatGPT

在 Django 中保存 unicode 字符串时出现 MySQL“不正确的字符串值”错误

尝试将 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 个字节导致上述错误?

这是 MySQL 问题,不是 Django:stackoverflow.com/questions/1168036/…

d
donturner

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

您不能使用 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 个字符。


这个答案需要更多的支持。谢谢!真正的问题是您的应用程序可能运行良好多年,直到有人尝试输入 4 字节字符。
这绝对是正确的答案。 OPTIONS 设置对于让 django 解码 emoji 字符并将它们存储在 MySQL 中至关重要。仅通过 SQL 命令将 mysql 字符集更改为 utf8mb4 是不够的!
无需将整个表的字符集更新为utf8mb4。只需更新必要列的字符集。正如@Xerion 所说,Django 设置中的 'charset': 'utf8mb4' 选项也很重要。最后,索引问题是一团糟。删除列上的索引,或使其长度不超过 191,或使用 TextField 代替!
我喜欢你对这句话的link这只是 MySQL 被故意和不可逆转的脑损伤的另一个案例。 :)
请注意,如果使用 mysql.connector.django 作为数据库后端,您还必须在 OPTIONS 中设置 'collation': 'utf8mb4_unicode_ci'
C
Community

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

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

呃,我尽可能地更改了所有字符集,直到我真正重新阅读这个答案:列可以有自己的字符集,独立于表和数据库。这太疯狂了,也正是我的问题。
这也适用于我,在 TextField 模型中使用 mysql 和默认值。
这解决了我的问题。我所做的唯一更改是使用 utf8mb4 和 utf8mb4_general_ci 而不是 utf8 / utf8_general_ci。
C
Chris

如果你有这个问题,这里有一个 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()

这个解决方案解决了我使用存储文件和目录路径的 django 应用程序的所有问题。折腾 dbname 作为您的 django 数据库并让它运行。像魅力一样工作!
在我在 db.close() 之前添加 db.commit() 之前,此代码对我不起作用。
此解决方案是否避免@markpasc 评论中讨论的问题:'...4-byte UTF-8 characters such as emoji in MySQL 5.1's 3-byte utf8 character set'
当我通过 django 管理员删除记录时,该解决方案对我有帮助,我在创建 o 编辑时没有任何问题......奇怪!我什至可以直接在数据库中删除
每次更改模型时都应该这样做吗?
V
Vanuan

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

CREATE DATABASE <dbname> CHARACTER SET utf8;

嗨,请帮助检查这个问题stackoverflow.com/questions/46348817/…
在我的例子中,我们的数据库是由 docker 创建的,所以为了修复我在 db:command: 中添加了以下内容:我的撰写文件中的指令:- --character-set-server=utf8
就如此容易。谢谢@Vanuan
如果这不是一个新项目,我们从 db 获取备份,删除它并使用 utf8 字符集重新创建它,然后恢复备份。我在我的项目中做到了这不是新的......
@followben 谢谢,这解决了我的问题。虽然我不得不使用 --character-set-server=utf8mb4
j
jack

我只是想出了一种避免上述错误的方法。

保存到数据库

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 或其他各种东西一起正常工作。
虽然存储转义码而不是字符似乎令人反感,但这可能是在 MySQL 5.1 的 3 字节 utf8 字符集中保存 4 字节 UTF-8 字符(如表情符号)的少数方法之一。
有一种称为 utf8mb4 的编码允许存储比基本多语言平面更多的内容。我知道,你会认为“UTF8”是完全存储 Unicode 所需要的。好吧,whaddaya 知道,它不是。请参阅dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html
@jack,您可能需要考虑将接受的答案更改为更有用的答案
这是一个可行的解决方法,但我也不建议使用它(正如@muudscope 所倡导的那样)。例如,我仍然无法将表情符号存储到 mysql 数据库中。有人实现了吗?
W
Wei An

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

请注意,这不能在 Django 中完成。


R
Ron

改进@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]) 。谢谢你的最佳答案。
T
Thomas Wouters

您不是在尝试保存 unicode 字符串,而是在尝试以 UTF-8 编码保存字节字符串。使它们成为实际的 unicode 字符串文字:

user.last_name = u'Slatkevičius'

或(当您没有字符串文字时)使用 utf-8 编码对其进行解码:

user.last_name = lastname.decode('utf-8')

@Thomas,我完全按照您所说的进行了尝试,但仍然会引发相同的错误。
R
Rishabh Jhalani

只需改变你的桌子,不需要任何东西。只需在数据库上运行此查询。 ALTER TABLE table_name转换为字符集 utf8

它肯定会起作用。


不简单:错误代码:1118。行大小太大。使用的表类型的最大行大小(不包括 BLOB)为 65535。这包括存储开销,请查看手册。您必须将某些列更改为 TEXT 或 BLOBs 。我可以改变它,但是 Django 会弄乱它