python - 使用 Django 将 Unicode 字符存储到 MySQL 时出现问题

Posted

技术标签:

【中文标题】python - 使用 Django 将 Unicode 字符存储到 MySQL 时出现问题【英文标题】:python - Problem storing Unicode character to MySQL with Django 【发布时间】:2010-11-09 07:16:19 【问题描述】:

我有字符串

 u"Played Mirror's Edge\u2122"

应该显示为

 Played Mirror's Edge™

但这是另一个问题。我手头的问题是我将其放入模型中,然后尝试将其保存到数据库中。又名:

a = models.Achievement(name=u"Played Mirror's Edge\u2122")
a.save()

我得到了:

'ascii' codec can't encode character u'\u2122' in position 13: ordinal not in range(128)

完整的堆栈跟踪(根据要求):

Traceback:
File "/var/home/ptarjan/django/mysite/django/core/handlers/base.py" in get_response
  86.                 response = callback(request, *callback_args, **callback_kwargs)
File "/var/home/ptarjan/django/mysite/yourock/views/alias.py" in import_all
  161.     types.import_all(type, alias)
File "/var/home/ptarjan/django/mysite/yourock/types/types.py" in import_all
  52.     return modules[type].import_all(siteAlias, alias)
File "/var/home/ptarjan/django/mysite/yourock/types/xbox.py" in import_all
  117.             achiever = self.add_achievement(dict, siteAlias, alias)
File "/var/home/ptarjan/django/mysite/yourock/types/base_profile.py" in add_achievement
  130.                 owner       = siteAlias,
File "/var/home/ptarjan/django/mysite/django/db/models/query.py" in get
  304.         num = len(clone)
File "/var/home/ptarjan/django/mysite/django/db/models/query.py" in __len__
  160.                 self._result_cache = list(self.iterator())
File "/var/home/ptarjan/django/mysite/django/db/models/query.py" in iterator
  275.         for row in self.query.results_iter():
File "/var/home/ptarjan/django/mysite/django/db/models/sql/query.py" in results_iter
  206.         for rows in self.execute_sql(MULTI):
File "/var/home/ptarjan/django/mysite/django/db/models/sql/query.py" in execute_sql
  1734.         cursor.execute(sql, params)
File "/var/home/ptarjan/django/mysite/django/db/backends/util.py" in execute
  19.             return self.cursor.execute(sql, params)
File "/var/home/ptarjan/django/mysite/django/db/backends/mysql/base.py" in execute
  83.             return self.cursor.execute(query, args)
File "/usr/lib/pymodules/python2.5/MySQLdb/cursors.py" in execute
  151.             query = query % db.literal(args)
File "/usr/lib/pymodules/python2.5/MySQLdb/connections.py" in literal
  247.         return self.escape(o, self.encoders)
File "/usr/lib/pymodules/python2.5/MySQLdb/connections.py" in string_literal
  180.                 return db.string_literal(obj)

Exception Type: UnicodeEncodeError at /import/xbox:bob
Exception Value: 'ascii' codec can't encode character u'\u2122' in position 13: ordinal not in range(128)

以及模型的相关部分:

class Achievement(MyBaseModel):
    name = models.CharField(max_length=100, help_text="A human readable achievement name")

我在我的 settings.py 中使用 MySQL 后端

DEFAULT_CHARSET = 'utf-8'

所以基本上,我到底应该如何处理所有这些 un​​icode 的东西?如果我远离有趣的字符集并坚持使用 UTF8,我希望这一切都能“正常工作”。唉,这似乎没那么容易。

【问题讨论】:

听起来好像不喜欢单引号(')字符... 怎么回事?我认为它在 \u2122 上很无聊...... 你能提供其余的堆栈跟踪吗?实际的数据库代码可能正确处理了您的 unicode 字符串,但是某处的某些日志记录代码搞砸了。 并尝试缩小问题:models.Achievement.objects.create(name=u"\u2122") models.Achievement.objects.create(name=u"Played Mirror's Edge") 你使用什么数据库排序规则? 【参考方案1】:

感谢所有在这里发帖的人。它确实有助于我的 unicode 知识(希望其他人也学到了一些东西)。

因为我试图简化我的问题并且没有提供所有信息,所以我们似乎都找错了树。似乎我没有使用“REAL” unicode 字符串,而是使用 BeautifulSoup.NavigableString 将它们自己表示为 unicode 字符串。所以所有的打印输出看起来都像 unicode,但实际上并非如此。

在 MySQLDB 库的某个深处,他们无法处理这些字符串。

这行得通:

>>> Achievement.objects.get(name = u"Mirror's Edge\u2122")
<Achievement: Mirror's Edge™>

另一方面:

>>> b = BeautifulSoup(u"<span>Mirror's Edge\u2122</span>").span.string
>>> Achievement.objects.get(name = b)
... Exceptoins ...
UnicodeEncodeError: 'ascii' codec can't encode character u'\u2122' in position 13: ordinal not in range(128)

但这有效:

>>> Achievement.objects.get(name = unicode(b))
<Achievement: Mirror's Edge™>

所以,再次感谢所有 unicode 帮助,我相信它会派上用场。但是现在...

警告:BeautifulSoup 不返回 REAL unicode 字符串,在对它们进行任何有意义的操作之前应该使用 unicode() 强制执行。

【讨论】:

谢谢,通过谷歌找到了这个并从答案中学到了很多东西。然后我在最后读到你在用美丽的汤。跟我一样。 :) 即使在 BeautifulSoup 返回的所有值上强制使用 unicode 之后,我也无法使其工作。我在打印到终端和插入 MySQL 时都遇到错误。错误的形式为''latin-1' codec can't encode character u'\u03bc' in position 545: ordinal not in range(256)' 顺便说一句,lxml 也会发生同样的事情。如果您将文本从lxml 直接传递到 MySQLdb(其类型为 &lt;type 'lxml.etree._ElementUnicodeResult'&gt;),则会收到相同的错误消息。【参考方案2】:

我同意尼古拉的观点。我已经遇到了使用 UTF-8 的问题,即使在纯 Python (2.5) 中也是如此。

我终于用上了unicode函数(?):

entry    = unicode(sys.stdin, ENCODING)

编码取决于语言环境,如果我没记错的话:

import sys, locale

ENCODING    = locale.getdefaultlocale()[1]
DEFAULT_ENCODING    = sys.getdefaultencoding()

也许看看Python Unicode HOWTO?

【讨论】:

【参考方案3】:

我在使用 mysql 和 postgres 时遇到了类似的问题,但使用 sqllite 没有问题。

这就是我用 postgres 解决问题的方法(没有用 mysql 测试这个技巧,但我认为它也会解决它)

在你处理 unicode 字符串的文件中做一个

from django.utils.safestring import SafeUnicode

并假设 unistr 是包含字符串的变量,执行

unistr = SafeUnicode(unistr)

就我而言,我是从网站上抓取的

出现问题的原始代码(ht 是 beautifulsoup 对象):-

keyword = ht.a.string

修复:-

keyword = SafeUnicode(ht.a.string)

我不知道 SafeUnicode 为何或在做什么,我只知道它解决了我的问题。

【讨论】:

来自 SafeUnicode 的文档 """ 一个 unicode 子类,专门标记为“安全”以用于 html 输出。“””我认为您只是使用该函数将其转换为 unicode。我确实使用了很多方法来获取数据,一些正则表达式 urllib2.open().read() 一些漂亮的汤。我以为 Beautiful Soup 默认使用 unicode...【参考方案4】:

几点说明:

Python 2.x 有两种字符串类型

“str”,它基本上是一个字节数组(所以你可以在里面存储任何你喜欢的东西) "unicode",内部为UCS2/UCS4编码的unicode

这些类型的实例被视为“解码”数据。内部表示是参考,因此您将外部数据“解码”到其中,然后将外部数据“编码”为某种外部格式。

一个好的策略是在数据进入系统时尽早解码,并尽可能晚地进行编码。尽量对系统中的字符串使用 unicode。 (在这方面我不同意 Nikolai)。

此编码方面适用于 Nicolai 的回答。他获取原始的 unicode 字符串,并将其编码为 utf-8。但这并没有解决问题(至少不是一般情况下),因为生成的字节缓冲区仍然包含范围之外的字节(127)(我没有检查对于 \u2122),这意味着您将再次遇到相同的异常。

Nicolai 的分析仍然认为您正在传递一个 unicode 字符串,但在系统的某个地方,这被视为 str 实例。如果在某个地方将 str() 函数应用于您的 unicode 参数就足够了。

在这种情况下,Python 使用所谓的默认编码,如果您不更改它就是 ascii。有一个函数 sys.setdefaultencoding 可以用来切换到例如utf-8,但该功能仅在有限的上下文中可用,因此您无法在应用程序代码中轻松使用它。

我的感觉是问题出在您调用的层的更深处。不幸的是,我无法评论 Django 或 MySQL/SQLalchemy,但我想知道在模型中声明“名称”属性时是否可以指定 unicode 类型。在字段级别处理类型信息将是一种很好的数据库实践。也许还有 CharField 的替代品?!

是的,您可以安全地将单引号 (') 嵌入双引号 (") 字符串中,反之亦然。

【讨论】:

谢谢,很好的信息。事实上,UTF8 也有与 ascii 相同的问题:unicode.encode(u"Played Mirror's Edge\u2122", 'utf8') "Played Mirror's Edge\xe2\x84\xa2"。我一直在尝试使用 unicode(虽然我正在这样做),并且我的数据库是用 utf8 编码的。【参考方案5】:

对我来说,撇号看起来很奇怪,不应该这样转义吗:

u"Played Mirror\'s Edge\u2122"

【讨论】:

给定的字符串和你的是等价的。将其输入到 python 解释器中。 >>> u"玩过魔镜\u2122" u"玩过魔镜\u2122" >>> u"玩过魔镜\u2122" u"玩过魔镜\u2122" 撇号不必转义。转义不必转义的字符只会混淆代码。但你说得对,应该对不赞成票发表评论。【参考方案6】:

您正在使用“unicode”类型的字符串。如果您的模型或 SQL 后端不支持它们或不知道如何转换为 UTF-8,则只需自己进行转换。坚持使用简单的字符串(python 类型 str)并像 in 一样转换

a = models.Achievement(name=u"Played Mirror's Edge\u2122".encode("UTF-8"))

【讨论】:

为什么对这个答案投反对票? Nikolai 似乎在我看来是在正确的轨道上注意到 Unicode not 与 UTF-8 相同... 如果我这样做,那么当我尝试在插入后打印模型时,我会从 force_unicode 得到 DjangoUnicodeDecodeError。如果我从数据库中获取它,那么它是完美的,但是打印最初插入的对象会抛出 DjangoUnicodeDecodeError。 :(【参考方案7】:

我昨天正在处理这个问题,我发现在连接字符串中添加“charset=utf8”和“use_unicode=1”可以正常工作(使用 SQLAlchemy,猜想是同样的问题)。

所以我的字符串看起来像: "mysql://user:pass@host:3306/database?use_unicode=1&charset=utf8"

【讨论】:

查看 django 文件,在 ./db/backends/mysql/base.py 它有 kwargs = 'conv': django_conversions, 'charset': 'utf8', 'use_unicode': True, 所以我认为它已经像这样连接了。

以上是关于python - 使用 Django 将 Unicode 字符存储到 MySQL 时出现问题的主要内容,如果未能解决你的问题,请参考以下文章

python 使用eval报错NameError: name ‘null’ is not defined

Linux 下 将使用Python-Django开发的web应用布置到服务器上(亲测有效)

使用 Python 3/Django,如何将 MySql 表数据导出为 YAML 文件?

使用 Django/Python 将批量 .csv 数据上传到 webapp 的好方法是啥?

如何在python文件中,引用django1.10的model

Python编码