Головная боль от Django с простой строкой не-ascii - PullRequest
8 голосов
/ 30 января 2010

Я только что создал следующую модель:

class Categoria(models.Model):
    nombre=models.CharField(max_length=30)
    padre=models.ForeignKey('self', blank=True, null=True)

    def __unicode__(self):
        return self.nombre

Затем регистрируется в интерфейсе администратора и syncdb'd

Все в порядке, если я просто добавлю простые символы ASCII. Но если я добавлю «Категорию» с именем «а» (что-то сказать), я получу:

Environment:

Request Method: GET
Request URL: http://192.168.2.103:8000/administracion/locales/categoria/
Django Version: 1.1.1
Python Version: 2.6.4
Installed Applications:
['django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.admin',
 'cruzandoelsuquiaDJ.locales']
Installed Middleware:
('django.middleware.common.CommonMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware')


Template error:
In template /usr/lib/pymodules/python2.6/django/contrib/admin/templates/admin/change_list.html, error at line 78
   Caught an exception while rendering: ('ascii', '\xc3\xa1', 0, 1, 'ordinal not in range(128)')
   68 :         {% endif %}


   69 :       {% endblock %}


   70 :       


   71 :       <form action="" method="post"{% if cl.formset.is_multipart %} enctype="multipart/form-data"{% endif %}>


   72 :       {% if cl.formset %}


   73 :         {{ cl.formset.management_form }}


   74 :       {% endif %}


   75 : 


   76 :       {% block result_list %}


   77 :           {% if action_form and actions_on_top and cl.full_result_count %}{% admin_actions %}{% endif %}


   78 :            {% result_list cl %} 


   79 :           {% if action_form and actions_on_bottom and cl.full_result_count %}{% admin_actions %}{% endif %}


   80 :       {% endblock %}


   81 :       {% block pagination %}{% pagination cl %}{% endblock %}


   82 :       </form>


   83 :     </div>


   84 :   </div>


   85 : {% endblock %}


   86 : 

Traceback:
File "/usr/lib/pymodules/python2.6/django/core/handlers/base.py" in get_response
  92.                 response = callback(request, *callback_args, **callback_kwargs)
File "/usr/lib/pymodules/python2.6/django/contrib/admin/options.py" in wrapper
  226.                 return self.admin_site.admin_view(view)(*args, **kwargs)
File "/usr/lib/pymodules/python2.6/django/views/decorators/cache.py" in _wrapped_view_func
  44.         response = view_func(request, *args, **kwargs)
File "/usr/lib/pymodules/python2.6/django/contrib/admin/sites.py" in inner
  186.             return view(request, *args, **kwargs)
File "/usr/lib/pymodules/python2.6/django/contrib/admin/options.py" in changelist_view
  986.         ], context, context_instance=context_instance)
File "/usr/lib/pymodules/python2.6/django/shortcuts/__init__.py" in render_to_response
  20.     return HttpResponse(loader.render_to_string(*args, **kwargs), **httpresponse_kwargs)
File "/usr/lib/pymodules/python2.6/django/template/loader.py" in render_to_string
  108.     return t.render(context_instance)
File "/usr/lib/pymodules/python2.6/django/template/__init__.py" in render
  178.         return self.nodelist.render(context)
File "/usr/lib/pymodules/python2.6/django/template/__init__.py" in render
  779.                 bits.append(self.render_node(node, context))
File "/usr/lib/pymodules/python2.6/django/template/debug.py" in render_node
  71.             result = node.render(context)
File "/usr/lib/pymodules/python2.6/django/template/loader_tags.py" in render
  97.         return compiled_parent.render(context)
File "/usr/lib/pymodules/python2.6/django/template/__init__.py" in render
  178.         return self.nodelist.render(context)
File "/usr/lib/pymodules/python2.6/django/template/__init__.py" in render
  779.                 bits.append(self.render_node(node, context))
File "/usr/lib/pymodules/python2.6/django/template/debug.py" in render_node
  71.             result = node.render(context)
File "/usr/lib/pymodules/python2.6/django/template/loader_tags.py" in render
  97.         return compiled_parent.render(context)
File "/usr/lib/pymodules/python2.6/django/template/__init__.py" in render
  178.         return self.nodelist.render(context)
File "/usr/lib/pymodules/python2.6/django/template/__init__.py" in render
  779.                 bits.append(self.render_node(node, context))
File "/usr/lib/pymodules/python2.6/django/template/debug.py" in render_node
  71.             result = node.render(context)
File "/usr/lib/pymodules/python2.6/django/template/loader_tags.py" in render
  24.         result = self.nodelist.render(context)
File "/usr/lib/pymodules/python2.6/django/template/__init__.py" in render
  779.                 bits.append(self.render_node(node, context))
File "/usr/lib/pymodules/python2.6/django/template/debug.py" in render_node
  71.             result = node.render(context)
File "/usr/lib/pymodules/python2.6/django/template/loader_tags.py" in render
  24.         result = self.nodelist.render(context)
File "/usr/lib/pymodules/python2.6/django/template/__init__.py" in render
  779.                 bits.append(self.render_node(node, context))
File "/usr/lib/pymodules/python2.6/django/template/debug.py" in render_node
  81.             raise wrapped

Exception Type: TemplateSyntaxError at /administracion/locales/categoria/
Exception Value: Caught an exception while rendering: ('ascii', '\xc3\xa1', 0, 1, 'ordinal not in range(128)')

Моя версия django - 1.1, а моя база данных - 5.1.37-1ubuntu5 с кодировкой utf8, а в таблице используется сопоставление utf8_bin.

Эта проблема кажется слишком простой, чтобы быть правдой, и я новичок в Django, поэтому заранее прошу прощения, если мне не хватает чего-то очень простого:)

Ответы [ 6 ]

17 голосов
/ 02 февраля 2010

Django обычно имеет очень хорошую поддержку Unicode (подробности см. В документации Django 1.1 «Данные Unicode» ). В моем коде я обнаружил, что, если у меня возникают проблемы с простыми функциями Unicode, обычно проблема заключается в том, что я плохо понимаю детали Django, а не в том, что в Django есть ошибка в поддержке Unicode.

Страница «Unicode Data» сообщает нам, что «Все бэкэнды базы данных Django ... автоматически преобразуют строки, извлеченные из базы данных, в строки Python Unicode. Вам даже не нужно сообщать Django, какую кодировку использует ваша база данных: это обрабатываются прозрачно. " Таким образом, ваш простой return self.nombre должен вернуть строку Python Unicode.

Однако на странице Django 1.1 «Базы данных» есть важное замечание о том, как серверная часть MySQL обрабатывает сопоставление utf8_bin:

... если вы действительно хотите учитывать регистр сравнения по определенному столбцу или таблица, вы бы изменить столбец или таблица для использования сортировки utf8_bin. Главное, о чем нужно знать в этом дело в том, что если вы используете MySQLdb 1.2.2, серверная часть базы данных в Django будет возвращать строки байтов (вместо Unicode-строк) для любого символа поля, которые он возвращает, получают от база данных. Это сильная вариация от обычной практики Джанго всегда возвращая строки Юникода. Это решать вам, разработчику, тот факт, что вы получите bytestrings, если вы настраиваете таблицы для использования сортировки utf8_bin. Сам Джанго должен работать плавно с такими столбцами, но если ваш код должен быть готов позвонить django.utils.encoding.smart_unicode () иногда, если он действительно хочет работать с непротиворечивыми данными ...

Итак, в вашем исходном примере в столбце «nombre» использовалась сортировка utf8_bin. Это означало, что self.nombre возвращал строку байтов Python. Когда вы помещаете его в выражение, которое требует строку Python Unicode, Python выполняет преобразование по умолчанию. Это эквивалент self.nombre.decode('ascii'). И, конечно же, .decode('ascii') терпит неудачу, когда встречает любой байт выше 0x7F, такой как байты UTF-8, которые кодируют «á».

Вы обнаружили два способа решения этой проблемы. Первый заключается в явном преобразовании строки байтов Python, возвращаемой self.nombre, в строку Python Unicode. Держу пари, что сработал бы следующий более простой код:

return self.nombre.decode('utf8')

Второй подход заключается в изменении сортировки MySQL для столбца «nombre», что заставляет серверную часть MySQL Django возвращать строки Python Unicode вместо необычных строк байтов. Тогда ваше оригинальное выражение дает строку Python Unicode:

return self.nombre

Надеюсь, это поможет.

7 голосов
/ 16 июня 2011

Эта проблема может быть решена путем небольшого изменения кода Django. Добавьте код ниже в django / utils / encoding.py

import sys
reload(sys)
sys.setdefaultencoding('utf-8')
4 голосов
/ 01 ноября 2011

У меня была эта проблема на производстве и никогда на сервере разработки.
Затем я понял, что новые таблицы были созданы с сопоставлением utf8_bin вместо utf8_general_ci.

Чтобы увидеть, какие таблицы требуют преобразования, введите

SHOW TABLE STATUS;

Затем конвертируйте те с utf8_bin, набрав

ALTER TABLE app_table CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;

Наконец, измените параметры сортировки по умолчанию, чтобы это больше не повторилось:

ALTER DATABASE my_database character set utf8 collate utf8_general_ci;
2 голосов
/ 30 января 2010

ОК ...

    return u"%s"%(self.nombre.decode('utf8'),)

делает свое дело.

Но также обнаружил, что изменение utf8_bin на utf8_general_ci делает свое дело, то есть self.nombre работает как положено.

1 голос
/ 10 сентября 2014

Я решил проблему, просто создав модель администратора для этой модели и включив все переменные в list_display.

0 голосов
/ 28 февраля 2013

У меня была похожая проблема, когда недавно я изменил таблицу MySQL, чтобы использовать сортировку utf8_bin при подготовке, в то время как в dev нет проблем (python2.7, Django1.4.2 в обеих средах). Я узнал, что в dev у меня MySQL-python 1.2.4c1, а в стадии подготовки - 1.2.3. Обновление до MySQL-python 1.2.4 решило проблему для меня.

...