Ввод администратора Django вызывает UnicodeDecodeError, как?

Сегодня я получил данные через администратора Django, которые не удалось закодировать. Почему-то кодировка данных не в юникоде. Как это возможно?

У меня есть свойство name в моей модели Client, которое возвращает данные в юникоде:

@property
def name(self):
    return u'{0} {1}'.format(self.firstname, self.lastname).strip()

Но это не работает:

>>> client
<Client: [Bad Unicode data]>

>>> client.lastname
'Dani\xc3\xabl'

>>> client.lastname.__class__
<type 'str'>

>>> u"{0} {1}".format(client.firstname, client.lastname)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 4: ordinal not in range(128)

Достаточно странно, кодирование имени/фамилии как обычной строки работает:

>>> "{0} {1}".format(client.firstname, client.lastname)
'Test Dani\xc3\xabl'

>>> "{0} {1}".format(client.firstname, client.lastname).decode('utf-8')
u'Test Dani\xebl'

Что здесь случилось? и как этот ввод попал в мою модель через админку?

Системный стек (это внешний сервер):

  • Debian 6.0.5 (сжать)
  • Джанго 1.4.1
  • Питон 2.6.6
  • MySQL 5.1.49
  • MySQL-питон == 1.2.2

Это соответствующий код модели:

class Client(models.Model):
    firstname = models.CharField(_("Firstname"), max_length=255)
    lastname = models.CharField(_("Lastname"), max_length=255)
    email = models.EmailField(_("Email"), unique=True, max_length=255)

    class Meta:
        db_table = u'clients'
        ordering = ('firstname', 'lastname', 'email')

    def __unicode__(self):
        return u'{0} <{1}>'.format(self.name, self.email)

    @property
    def name(self):
        return u'{0} {1}'.format(self.firstname, self.lastname).strip()

person vdboor    schedule 01.09.2012    source источник
comment
На всякий случай, я так понимаю, что firstname и lastname это поля? Не могли бы вы опубликовать соответствующий код модели?   -  person Thomas Orozco    schedule 01.09.2012


Ответы (1)


Это, вероятно, связано с сопоставлением, которое вы используете для своей базы данных MySQL.

Действительно, поведение Django заключается в том, чтобы всегда возвращать unicodestrings при извлечении данных из базы данных, что будет работать с вашим кодом, поскольку в этом нет ничего плохого.

Однако, как вы можете видеть в документации django по настройкам базы данных, параметры сопоставления раздела, использование MySQLdb версии 1.2.2 с базой данных utf8_bincollated MySQL приведет к тому, что вы получите не строки Unicode, а строки байтов при извлечении полей символов из базы данных.

Возможно, вы захотите изучить эту проблему (то есть проверить настройки сопоставления MySQL), но вполне вероятно, что ваша проблема исходит оттуда.

Если это так, вам придется декодировать вручную любой ввод, который вы получаете от MySQL. Кроме того, вы можете изменить параметры сортировки вашей базы данных.

Вы можете использовать SHOW TABLE STATUS FROM %YOURDB% для сопоставления таблиц в базе данных.


Выдержка из соответствующего раздела документации:

По умолчанию с базой данных UTF-8 MySQL будет использовать параметры сортировки utf8_general_ci_swedish. Это приводит к тому, что все сравнения строк на равенство выполняются без учета регистра. То есть Fred и freD считаются равными на уровне базы данных. Если у вас есть уникальное ограничение для поля, было бы недопустимо пытаться вставить как aa, так и AA в один и тот же столбец, поскольку они сравниваются как равные (и, следовательно, неуникальные) с сопоставлением по умолчанию.

Во многих случаях это значение по умолчанию не будет проблемой. Однако, если вам действительно нужны сравнения с учетом регистра в определенном столбце или таблице, вы должны изменить столбец или таблицу, чтобы использовать сопоставление utf8_bin. Главное, о чем следует помнить в этом случае, это то, что если вы используете MySQLdb 1.2.2, серверная часть базы данных в Django будет возвращать строки байтов (вместо строк Unicode) для любых символьных полей, которые он получает из базы данных. Это серьезное отличие от обычной практики Django всегда возвращать строки в формате Unicode.

person Thomas Orozco    schedule 01.09.2012
comment
Оч... кажется, так оно и есть. Любая попытка изменить соединение OPTIONS пока не удалась. Таблицы действительно находятся в latin1_swedish_ci сопоставлении. Однако до сих пор мне не удалось исправить вывод MySQLdb/Django. - person vdboor; 01.09.2012
comment
@vdboor Вы всегда можете изменить сопоставление таблицы/базы данных: dev.mysql.com/doc/refman/5.1/en/charset-table.html - person Thomas Orozco; 01.09.2012