【问题标题】:Django Model __unicode__ raising exception when loggingDjango Model __unicode__ 在记录时引发异常
【发布时间】:2015-11-18 13:19:33
【问题描述】:

我有一个如下所示的模型类:

class Address(models.Model):
    # taking length of address/city fields from existing UserProfile model
    address_1 = models.CharField(max_length=128,
                                 blank=False,
                                 null=False)

    address_2 = models.CharField(max_length=128,
                                 blank=True,
                                 null=True)

    address_3 = models.CharField(max_length=128,
                                 blank=True,
                                 null=True)

    unit = models.CharField(max_length=10,
                            blank=True,
                            null=True)

    city = models.CharField(max_length=128,
                            blank=False,
                            null=False)

    state_or_province = models.ForeignKey(StateOrProvince)

    postal_code = models.CharField(max_length=20,
                                   blank=False,
                                   null=False)

    phone = models.CharField(max_length=20,
                             blank=True,
                             null=True)

    is_deleted = models.BooleanField(default=False,
                                     null=False)

    def __unicode__(self):
        return u"{}, {} {}, {}".format(
            self.city, self.state_or_province.postal_abbrev, self.postal_code, self.address_1)

关键是__unicode__ 方法。我有一个客户模型,该模型具有该表的外键字段,并且正在执行以下日志记录:

log.debug(u'Generated customer [{}]'.format(vars(customer)))

这很好用,但是如果 address_1 字段值包含非 ascii 值,比如说

57562 Vån Ness Hwy

系统抛出以下异常:

UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 345: ordinal not in range(128)

我在 django/db/models/base.py 中找到了一个奇怪的方法:

def __repr__(self):
        try:
            u = six.text_type(self)
        except (UnicodeEncodeError, UnicodeDecodeError):
            u = '[Bad Unicode data]'
        return force_str('<%s: %s>' % (self.__class__.__name__, u))

如您所见,此方法被调用到 force_str,但未正确处理。这是一个错误吗?如果在我的对象上调用了 unicode,不应该一切都在 unicode 中吗?

【问题讨论】:

    标签: django python-2.7 logging unicode django-1.7


    【解决方案1】:

    尝试decode 用于非 utf-8 字符:

    def __unicode__(self):
            return u"{}, {} {}, {}".format(
                self.city, self.state_or_province.postal_abbrev, self.postal_code, self.address_1.decode('utf-8'))
    

    【讨论】:

    • 所以,我想我很困惑为什么会调用 repr。这是 unicode 的问题,还是 repr 期望 str 对象的问题?
    • 我认为调用 repr 是因为它正在调试,请阅读此处了解差异stackoverflow.com/q/1436703/2343488
    • 注意unicode是方法名,返回的字符串可以用任意字符集编码
    • 我认为这是我对 unicode 对象的 python 知识搅浑水的地方。您可以有一个 unicode 对象但不能对其进行编码?我认为unicode字符串必须是utf-8。当我使用 encode('utf-8') 函数时,我的结果对象是一个 str 对象,我认为我不应该从 unicode 方法返回
    【解决方案2】:

    根据docs,当一个python对象作为参数传递给'{}'.format(obj)时,

    一般约定是空格式字符串 ("") [在 "{}" 内] 生成 与您在值上调用 str() 的结果相同。

    这意味着您实际上是在调用str(vars(customer)),而vars(customer) 返回一个dict。

    在dict 上调用str() 将在其键和值上调用repr(),否则你会得到模棱两可的输出(例如str(1) == str('1') == '1' 但repr(1) == '1' and repr('1') == '"1"'(参见Difference between __str__ and __repr__ in Python)

    因此,repr() 仍会在您的 Address 上被调用,这会返回一个字符串。

    现在 Python 2 - https://stackoverflow.com/a/3627835/648176 不允许从 repr() 返回 unicode,因此您需要在模型中覆盖 __str__() 以使其处理解码为 ascii (Django docs),或者执行类似:

    string_dict = {str(k): str(v) for (k, v) in vars(customer).items()}
    log.debug(u'Generated customer [{}]'.format(string_dict))
    

    【讨论】:

      【解决方案3】:

      这更像是一个漂亮的答案,但我仍然会把我的两分钱扔到一堆。只需将您正在使用的“logging.Handler”子类化,并更改“emit”方法(如果它是导致异常的方法)。

      优点

      非常容易设置。设置后,无需对任何模型/数据执行任何操作。

      缺点

      结果是不会出现 UnicodeErrors,但日志文件将在曾经有 unicode 标记的地方出现“以反斜杠开头的奇怪字符串”。例如?会变成'\xf0\x9f\xa6\x84\'。也许您可以在需要时使用脚本将 '\xf0\x9f\xa6\x84\' 转换回日志文件中的 unicode。

      步骤是

      1) 制作一个“custom_logging.py”,您可以将其导入到您的 settings.py 中

      from logging import FileHandler
      
      class Utf8FileHandler(FileHandler):
          """
                This is a hack-around version of the logging.Filehandler
      
              Prevents errors of the type
              UnicodeEncodeError: 'charmap' codec can't encode character '\U0001f984' in position 150: character maps to <undefined>
          """
          def __init__(self, *args, **kwargs):
              FileHandler.__init__(self, *args, **kwargs)
      
          def emit(self, record):
              """
              Emit a record.
      
              If a formatter is specified, it is used to format the record.
              The record is then written to the stream with a trailing newline.  If
              exception information is present, it is formatted using
              traceback.print_exception and appended to the stream.  If the stream
              has an 'encoding' attribute, it is used to determine how to do the
              output to the stream.
              """
              try:
                  msg = self.format(record)
                  stream = self.stream
                  stream.write(msg)
                  stream.write(self.terminator)
                  self.flush()
              except Exception:
                  # The hack.
                  try:
                      stream.write(str(msg.encode('utf-8'))[2:-1])
                      stream.write(self.terminator)
                      self.flush()
                  # End of the hack.
                  except Exception:
                      self.handleError(record)
      

      2) 在您的 settings.py 中,使用您自定义的文件处理程序,如下所示(将 LOGGING['handlers']['file']['class'] 设置为指向 custom_logging 模块。):

      LOGGING = {
          'version': 1,
          'disable_existing_loggers': False,
          'formatters': {
              'verbose': {
                  'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
              },
          },
          'handlers': {
              'file': {
                  'level': 'DEBUG',
                  'class': 'config.custom_logging.Utf8FileHandler',
                  'filename': secrets['DJANGO_LOG_FILE'],
                  'formatter': 'verbose',
              },
          },
          'loggers': {
              'django': {
                  'handlers': ['file'],
                  'level': 'DEBUG',
                  'propagate': True,
              },
          },
      }
      

      【讨论】:

        猜你喜欢
        • 2013-11-11
        • 2019-03-11
        • 2016-07-14
        • 2021-08-16
        • 1970-01-01
        • 1970-01-01
        • 2021-12-13
        • 2017-01-30
        • 1970-01-01
        相关资源
        最近更新 更多