无法将 character_set_results 设置为 latin1

2 投票
3 回答
1092 浏览
提问于 2025-04-20 00:16

我决定第一次使用Django 1.7和Python 3。
我需要处理一个包含utf8数据的旧latin1数据库。我知道这很糟糕,但数据库太大了,真的没办法改动。所以我尝试了以下方法:

DATABASES = {
    'ENGINE' : 'django.db.backends.mysql', // using MySQL-python fork with support for py3
    ...
    'OPTIONS' : {
        'init_command': "SET character_set_results = 'latin1'",
        #'read_default_file': '/etc/my.cnf.d/client.cnf', // I've also tried this one
    }
}

我还尝试了Oracle的python-mysql-connector,设置如下:

DATABASES = {
    'ENGINE' : 'mysql.connector.django', // using MySQL-python fork with support for py3
    'OPTIONS' : {
        'option_files': ['/etc/my.cnf.d/client.cnf'],
    }
}

/etc/my.cnf.d/client.cnf

[client]
init-command='SET character_set_results = "latin1"'
# password, host, username

在这两种情况下,我都能连接到数据库,但似乎Django把字符集结果设置回了utf8。

我尝试了以下方法:

from django.db import connection

with connection.cursor() as c:
   // I expect variable to be 'latin1'
   c.execute("show variables like 'character_set_results%'")
   c.fetchone() // returns ('character_set_results', 'utf8')

   // here I try to set it manually
   c.execute("SET character_set_results = 'latin1'")
   c.execute("show variables like 'character_set_results%'")
   c.fetchone() // returns ('character_set_results', 'latin1') // now it's OK
  • 我确定Django使用的是client.cfg文件和正确的[section],因为它包含了用户名和密码,并且成功连接到了数据库。
  • 当我在Linux终端使用mysql命令时,它使用的是同一个配置文件,一切都按预期工作。

所以我猜Django以某种方式强制将character_set_results变量设置为utf8。这可能吗?有没有办法解决这个问题?

非常感谢!

3 个回答

0

在你使用的客户端配置文件 client.cfg 中,针对 MySQL 连接器,不要使用 init-command 这个选项,因为连接器会忽略它。你可以用 write 和 charset=latin1 来代替,这样就可以正常工作了。

[client]
charset=latin1
# password, host, username
1

这不是一个完整的答案,但评论太长了,所以我就单独说一下。

Django的MySQL封装默认会在DatabaseWrapper.get_connection_params()中设置kwargs['charset']='utf8'。这个设置会被传递给MySQLdb的Connection.__init__,而这个文档中提到:

字符集
如果提供了这个参数,连接的字符集将会被更改为这个字符集(MySQL-4.1及更新版本)。这意味着
use_unicode=True。

所以一个起点可能是直接在你的OPTIONS字典中添加"charset":"latin1"

警告:我不确定这样做能否解决你的问题,甚至可能会引发其他问题,但把utf8编码的数据放在一个latin1的数据库里,肯定不是个好主意 :-/(我也经历过这个,我能理解你的痛苦)。

2

我终于搞明白了(我也不知道为什么每次在Stack Overflow发帖后过一段时间才找到解决办法)

from django.db.backends.signals import connection_created

def connection_setup(**kwargs):
    conn = kwargs['connection']
    with conn.cursor() as cursor:
        cursor.execute("SET character_set_results = 'latin1'")
        cursor.close()

我之前试过用Oracle的 python-mysql-connector,结果出现了

RuntimeError: maximum recursion depth exceeded in comparison

但是用 MySQL-driver 的py3版本就没问题。我猜这可能是 python-mysql-connector 或者 Django 的一个bug,我会去报告一下。希望这能帮到别人。

撰写回答