【问题标题】:storing Unicode subclasses to MySQL with Django使用 Django 将 Unicode 子类存储到 MySQL
【发布时间】:2016-09-29 13:45:24
【问题描述】:

我在 MySQL 中保存 Unicode 字符时遇到问题。

    Exception Type: UnicodeEncodeError
    Exception Value:    
'ascii' codec can't encode character u'\xed' in position 39: ordinal not in range(128)

    Exception Location: /home/truhlik/.virtualenvs/reality/local/lib/python2.7/site-packages/MySQLdb/connections.py in string_literal, line 204
    Python Executable:  /home/truhlik/.virtualenvs/reality/bin/python
    Python Version: 2.7.11

MySQL 库中出现错误:

    def _get_string_literal():
        def string_literal(obj, dummy=None):
            # try:
            return db.string_literal(obj) ...
            # except UnicodeEncodeError:
            #    return db.string_literal(unicode(obj).encode("utf-8"))
        return string_literal

变量:

obj = u'temp/files_widget/2016-05-31-15-00/1/Sn\xedmek obrazovky po\u0159\xedzen\xfd 2016-05-23 10-34-59.png'

我认为问题与这个问题有关: python - Problem storing Unicode character to MySQL with Django

我的“obj”变量不是纯 Unicode,而是 ImagePath 类实例。

class ImagePaths(unicode):
    item_class = ImagePath

问题是我不知道应该实施哪种方法来解决问题。

注意: 我的修复在上面的代码中有注释。但这不是很干净的解决方案。直接写在 MySQL 库中。

更新 #1:

完整的追溯:

        /home/truhlik/Dropbox/web/reality/permissions/models.py in save
        super(CustomModel, self).save(*args, **kwargs) ...

/home/truhlik/.virtualenvs/reality/local/lib/python2.7/site-packages/django/db/models/base.py in save
                       force_update=force_update, update_fields=update_fields) ...

/home/truhlik/.virtualenvs/reality/local/lib/python2.7/site-packages/django/db/models/base.py in save_base
            updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields) ...

/home/truhlik/.virtualenvs/reality/local/lib/python2.7/site-packages/django/db/models/base.py in _save_table
                                      forced_update) ...

/home/truhlik/.virtualenvs/reality/local/lib/python2.7/site-packages/django/db/models/base.py in _do_update
        return filtered._update(values) > 0 ...

/home/truhlik/.virtualenvs/reality/local/lib/python2.7/site-packages/django/db/models/query.py in _update
        return query.get_compiler(self.db).execute_sql(CURSOR) ...

/home/truhlik/.virtualenvs/reality/local/lib/python2.7/site-packages/django/db/models/sql/compiler.py in execute_sql
        cursor = super(SQLUpdateCompiler, self).execute_sql(result_type) ...

/home/truhlik/.virtualenvs/reality/local/lib/python2.7/site-packages/django/db/models/sql/compiler.py in execute_sql
            cursor.execute(sql, params) ...

/home/truhlik/.virtualenvs/reality/local/lib/python2.7/site-packages/django/db/backends/utils.py in execute
            return super(CursorDebugWrapper, self).execute(sql, params) ...

/home/truhlik/.virtualenvs/reality/local/lib/python2.7/site-packages/django/db/backends/utils.py in execute
                return self.cursor.execute(sql, params) ...

/home/truhlik/.virtualenvs/reality/local/lib/python2.7/site-packages/django/db/backends/mysql/base.py in execute
            return self.cursor.execute(query, args) ...

/home/truhlik/.virtualenvs/reality/local/lib/python2.7/site-packages/MySQLdb/cursors.py in execute
                query = query % tuple([db.literal(item) for item in args]) ...

/home/truhlik/.virtualenvs/reality/local/lib/python2.7/site-packages/MySQLdb/connections.py in literal
        return self.escape(o, self.encoders) ...

/home/truhlik/.virtualenvs/reality/local/lib/python2.7/site-packages/MySQLdb/connections.py in string_literal
                return db.string_literal(obj) 

使用此连接设置:

'default': {
    'ENGINE': 'django.db.backends.mysql',
    'NAME': 'reality_devel',
    'USER': 'reality_devel',
    'HOST': 'mail.it-poradce.cz',
    'PORT': '3306',
    'OPTIONS': {
        'charset': 'utf8',
        'use_unicode': True,
    }
}

MySQL数据库配置COLLATION 'utf8_general_ci' DEFAULT CHARACTER SET 'utf8'

发现几个问题直接报告给 Django 和 MySQL,这可能与我的问题有关。 http://bugs.mysql.com/bug.php?id=79993https://code.djangoproject.com/ticket/22377 但我不确定。

更新 #2:

我正在尝试使用此字段保存图像的路径。

images = files_widget.ImagesField(_(u'Obrázky'), blank=True, null=True, help_text=HELP_TEXT_IMAGES)

该字段是这样实现的:

class FilesField(models.TextField):
    description = _("Files")
    attr_class = controllers.FilePaths

    def __init__(self, *args, **kwargs):
        self.accept = kwargs.pop('accept', None)
        super(FilesField, self).__init__(*args, **kwargs)

    def contribute_to_class(self, cls, name):
        super(FilesField, self).contribute_to_class(cls, name)
        receiver(post_save, sender=cls)(manage_files_on_disk)
        setattr(cls, self.name, controllers.FilesDescriptor(self))

    def save_form_data(self, instance, data):
        save_all_data(self, instance, data)
        super(FilesField, self).save_form_data(instance, data)

    def formfield(self, default_widget=None, **kwargs):
        if not default_widget:
            default_widget = FilesWidget(field=self, accept=self.accept)
        defaults = formfield_defaults(self, default_widget, **kwargs)
        return super(FilesField, self).formfield(**defaults)

class ImagesField(FilesField):
    description = _("Images")
    attr_class = controllers.ImagePaths

    def formfield(self, default_widget=None, **kwargs):
        if not default_widget:
            default_widget = ImagesWidget(field=self, accept=self.accept)
        defaults = formfield_defaults(self, default_widget, **kwargs)
        return super(ImagesField, self).formfield(**defaults)

它正在使用这个应用程序:django-files-widget ...所以如果您需要查看更多代码,那么您可以在 GitHub 上查看。 抱歉,无法发布完整网址。

试图找到我应该在哪里......

正确转换为 unicode

但不要弄明白。

更新 #3:

添加结果:SHOW VARIABLES LIKE 'char%'

character_set_client - utf8mb4
character_set_connection - utf8mb4
character_set_database - utf8
character_set_filesystem - binary
character_set_results - utf8mb4
character_set_server - latin1
character_set_system - utf8
character_sets_dir - /usr/share/mysql/charsets/

【问题讨论】:

    标签: python mysql django unicode


    【解决方案1】:

    看起来您正在向库代码发送一个unicode 对象,它希望在该代码中接收一个str 对象。从您发布的代码中没有太多上下文可以参考(尝试发布整个回溯),但是通过修改 MySQLdb 库来处理异常并不是正确的方法。您应该在调用该库的客户端代码中处理它。在您的回溯中找到引发异常的行之前的点,然后将您的 try: except: 块移到那里。

    可能导致此问题的其他原因是您的 Connection 对象是使用 use_unicode = False 创建的。

    编辑:下面的一些示例代码。由于您没有发布 CustomClass 的代码,因此我编写了以下简单的类,它工作得很好。

    在models.py中:

    from __future__ import unicode_literals
    
    from django.db import models
    
    # Create your models here.
    class CustomClass(models.Model):
        path = models.CharField(max_length=200)
    

    我使用与您相同的OPTIONS 设置我的数据库。然后从 shell 测试:

    >>> c = CustomClass()
    >>> c.path = u'temp/files_widget/2016-05-31-15-00/1/Sn\xedmek obrazovky po\u0159\xedzen\xfd 2016-05-23 10-34-59.png'
    >>> c.save()
    >>>
    

    也许您在代码中的某处执行了一些非标准的操作,从而导致了UnicodeEncodeError。也许在您的自定义 save() 方法或之前的一些其他客户端代码中。也许您正在使用自定义的 Field 子类,它不能正确转换为 unicode。

    【讨论】:

    • 看起来您的配置有误,但我无法确定确切的位置。您更新中的链接指的是UnicodeDecodeError 而不是UnicodeEncodeError,这就是您所得到的。您得到的异常来自尝试将 unicode 对象转换为 str 对象的代码。试试str(u'temp/files_widget/2016-05-31-15-00/1/Sn\xedmek obrazovky po\u0159\xedzen\xfd 2016-05-23 10-34-59.png') 看看我的意思。
    • 也许您使用的自定义 Field 子类没有正确转换为 unicode。 ...这可能是问题所在。我使用 CustomField 实现添加了我的代码。
    • 您还需要查看该字段的值是如何分配的。您是否尝试过手动分配,然后像我上面所做的那样使用 Django shell 保存模型?您在上面发布的两个类似乎没有对其值做任何特殊的事情,这会导致此错误。所以很明显是你正在做的其他事情导致了这一切。
    • 手动试试,还是一样:>>>e.images u'' >>> e.images = u'temp/files_widget/2016-05-31-15-00/1/Sn\xedmek obrazovky po\u0159\xedzen\xfd 2016-05-23 10-34-59.png' >>> e.save() >>> UnicodeEncodeError: 'ascii' codec can't encode character u'\xed' in position 39: ordinal not in range(128)
    【解决方案2】:

    Hex ed 不是 Unicode,也不是 utf8。也许您期待í?这就是 latin1 编码为 ed 的内容。

    ed 是从哪里来的?要么将其更改为以 utf8 编码内容,要么声明您使用的是 latin1,而不是 utf8。

    Python 提示:http://mysql.rjweb.org/doc.php/charcoll#python
    Django 提示:http://mysql.rjweb.org/doc.php/charcoll#other_computer_languages

    【讨论】:

    • 感谢您的建议。该字符串来自通过 Form 上传到 Django 的 FILE 名称。所以我想它应该已经是utf8了。如果不是,我应该将编码放在哪里(以 init 形式或模型保存)?我的第二点......如果我对 instance.name 使用完全相同的字符串,它会正确保存到 DB。所以我仍然认为问题出在 CustomField 类及其实现中。
    • Obrázky是表列名吗?请提供SHOW VARIABLES LIKE 'char%';。其中之一可能控制如何处理列(等)名称。另外,你有关于_(u'...') 做什么的文档吗?它需要生成utf8,不是unicode,不是html实体。
    • 我已经用查询结果更新了我的任务。
    猜你喜欢
    • 2010-11-09
    • 2014-01-08
    • 1970-01-01
    • 1970-01-01
    • 2012-03-04
    • 1970-01-01
    • 2011-03-20
    • 2011-02-11
    • 2019-07-10
    相关资源
    最近更新 更多