Я просто создал следующую модель:
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. Но если я добавляю "Categoria", названный "á" (для высказывания чего-то), я добираюсь:
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 новичок, таким образом, я сожалею заранее, если я пропускаю что-то очень простое:)
Django в целом имеет очень хорошую поддержку Unicode (подробности см. в документации Django 1.1 "Unicode data"). В своем коде я обнаружил, что если у меня возникают проблемы с простыми функциями Unicode, то проблема обычно заключается в том, что я плохо понимаю детали Django, а не в том, что в Django есть ошибка в поддержке Unicode.
Страница "Unicode Data" говорит нам, что "Все бэкенды баз данных Django ... автоматически преобразуют строки, полученные из базы данных, в строки Python Unicode. Вам даже не нужно сообщать Django, какую кодировку использует ваша база данных: это делается прозрачно." Таким образом, ваш простой return self.nombre
должен вернуть строку Python Unicode.
Однако, на странице Django 1.1 "Databases" есть важное замечание о том, как бэкенд MySQL обрабатывает utf8_bin
collation:
...если вы действительно хотите чувствительные к регистру сравнения в определенном столбце или таблице, вы измените столбец или таблицы на использование коллизии utf8_bin. Главное, о чем следует помнить в этом случае это то, что если вы используете MySQLdb 1.2.2, то бэкенд базы данных в Django будет возвращать байтовые строки (вместо вместо строк юникода) для любых символьных полей, которые он получает из базы данных. Это сильное отклонение от обычной практики Django всегда возвращать строки юникода. Это зависит от вас, разработчика, чтобы справиться с тем. тот факт, что вы будете получать байтовые строки, если вы настроите вашу таблицу(и) на использование коллизии utf8_bin. Сам Django должен работать без проблем с такими колонками, но если ваш код должен быть готов к вызову 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
Надеюсь, это поможет.
Хорошо...
return u"%s"%(self.nombre.decode('utf8'),)
делает трюк.
Но также выяснилось, что замена utf8_bin на utf8_general_ci делает трюк, т.е. self.nombre работает, как и ожидалось.