UnicodeDecodeError с использованием Django и форматированных строк
Я написал небольшой пример проблемы для всех, чтобы узнать, что происходит с использованием Python 2.7 и Django 1.10.8
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, unicode_literals, print_function
import time
from django import setup
setup()
from django.contrib.auth.models import Group
group = Group(name='schön')
print(type(repr(group)))
print(type(str(group)))
print(type(unicode(group)))
print(group)
print(repr(group))
print(str(group))
print(unicode(group))
time.sleep(1.0)
print('%s' % group)
print('%r' % group) # fails
print('%s' % [group]) # fails
print('%r' % [group]) # fails
Выход со следующим выходом + трассировка
$ python .PyCharmCE2017.2/config/scratches/scratch.py
<type 'str'>
<type 'str'>
<type 'unicode'>
schön
<Group: schön>
schön
schön
schön
Traceback (most recent call last):
File "/home/srkunze/.PyCharmCE2017.2/config/scratches/scratch.py", line 22, in <module>
print('%r' % group) # fails
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 11: ordinal not in range(128)
Кто-нибудь знает, что здесь происходит?
Ответы
Ответ 1
Проблема заключается в том, что вы интерполируете UTF-8 bytestrings в строку Unicode. Строка '%r'
- это строка Юникода, потому что вы использовали from __future__ import unicode_literals
, но repr(group)
(используемый заполнителем %r
) возвращает байтовую строку. Для моделей Django repr()
может включать данные Unicode в представлении, закодированные в байтах, используя UTF-8. Такие представления не являются безопасными для ASCII.
В вашем конкретном примере repr()
на вашем экземпляре Group
создается байтовая строка '<Group: sch\xc3\xb6n>'
. Интерполяция в строку Unicode вызывает неявное декодирование:
>>> u'%s' % '<Group: sch\xc3\xb6n>'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 11: ordinal not in range(128)
Обратите внимание, что я не использовал from __future__ import unicode_literals
в моем сеансе Python, поэтому строка '<Group: sch\xc3\xb6n>'
не является объектом unicode
, это объект str
bytestring!
В Python 2 вам следует избегать смешивания строк Unicode и байтов. Всегда явно нормализуйте свои данные (кодирование Unicode в байтах или декодирование байтов в Unicode).
Если вы должны использовать from __future__ import unicode_literals
, вы все равно можете создать bytestrings с помощью префикса b
:
>>> from __future__ import unicode_literals
>>> type('') # empty unicode string
<type 'unicode'>
>>> type(b'') # empty bytestring, note the b prefix
<type 'str'>
>>> b'%s' % b'<Group: sch\xc3\xb6n>' # two bytestrings
'<Group: sch\xc3\xb6n>'
Ответ 2
Мне было трудно найти общее решение вашей проблемы.
__repr__()
то, что я понимаю, должен возвращать str, любые усилия по изменению, которые, похоже, вызывают новые проблемы.
Относительно того, что метод __repr__()
определен вне проекта, вы можете перегружать методы. Например
def new_repr(self):
return 'My representation of self {}'.format(self.name)
Group.add_to_class("__repr__", new_repr)
Единственное решение, которое я могу найти, - это явно указать интерпретатору, как обрабатывать строки.
from __future__ import unicode_literals
from django.contrib.auth.models import Group
group = Group(name='schön')
print(type(repr(group)))
print(type(str(group)))
print(type(unicode(group)))
print(group)
print(repr(group))
print(str(group))
print(unicode(group))
print('%s' % group)
print('%r' % repr(group))
print('%s' % [str(group)])
print('%r' % [repr(group)])
# added
print('{}'.format([repr(group).decode("utf-8")]))
print('{}'.format([repr(group)]))
print('{}'.format(group))
Работа со строками в python 2.x - беспорядок.
Надеюсь, что это привносит некоторый свет в то, как работать (что единственный способ найти) проблему.
Ответ 3
Я думаю, что реальная проблема в коде django.
Сообщалось шесть лет назад:
https://code.djangoproject.com/ticket/18063
Я думаю, что патч для django решит его:
def __repr__(self):
return self.....encode('ascii', 'replace')
Я думаю, что метод repr() должен возвращать "7 бит ascii".
Ответ 4
Если это так, нам нужно переопределить метод unicode с помощью нашего настраиваемого метода. Попробуйте ввести код ниже. Это будет работать. Я протестировал его.
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
from django.contrib.auth.models import Group
def custom_unicode(self):
return u"%s" % (self.name.encode('utf-8', 'ignore'))
Group.__unicode__ = custom_unicode
group = Group(name='schön')
# Tests
print(type(repr(group)))
print(type(str(group)))
print(type(unicode(group)))
print(group)
print(repr(group))
print(str(group))
print(unicode(group))
print('%s' % group)
print('%r' % group)
print('%s' % [group])
print('%r' % [group])
# output:
<type 'str'>
<type 'str'>
<type 'unicode'>
schön
<Group: schön>
schön
schön
schön
<Group: schön>
[<Group: schön>]
[<Group: schön>]
Ссылка: https://docs.python.org/2/howto/unicode.html
Ответ 5
Я не знаком с Django. Ваша проблема, кажется, представляет текстовые данные в ASCI, который фактически находится в Юникоде. Попробуйте модуль unidecode в Python.
from unidecode import unidecode
#print(string) is replaced with
print(unidecode(string))
Обратитесь Unidecode