1. Антон Силин

    09.08.2012

    1 ↑
    0 ↓
    Приветствую!

    Где-то я накосячил, что-то связанное с юникодом в своем django-проекте. Однако это не вызывает никаких проблем, пока все скпипты и вьюхи работают правильно - сайт работает.

    Но вдруг где-то в коде я допускаю ошибку (скобку забыл, обратился к несуществующему свойству модели или просто опечатка). Django начинает генерить для меня красивую страничку с ошибкой, со всеми свистелками и перделками, как вдруг - натыкается на ту самую неизвестную ошибку с юникодом в моем проекте. И вуаля - генерится новое сообщениеоб ошибке - UnicodeDecodeError вместо предыдущего, которое я так в итоге не вижу.

    Итог: не понятно, откуда вылез UnicodeDecodeError, непонятно, где я допустил ошибку, из-за которой django и захотел порадовать меня красивой error-page. Где вообще ошибку искать?

    Перешел на django после php, делаю на нем свой стартап - всем был доволен и счастлив, пока на полпути не вляпался в UnicodeDecodeError. Ну как так, а? Даже в богомерзком php всегда можно получить адекватное сообщение об ошибке, а с джангой - беда какая-то...

    Как вообще в общем случае искать источник ошибки UnicodeDecodeError?
    P.S. Django 1.4.1, начинал делать еще на 1.3.1
  2. Хм, никогда не возникало проблемы найти такую ошибку. Traceback же есть.

    А вообще - пишите тесты :)

  3. Mikhail Korobov

    10.08.2012

    1 ↑
    0 ↓

    А трейс?)

    Такие UnicodeDecodeError чаще всего означают, что питон где-то пытается строку преобразовать байтовую строку в юникод неявно (например, при конкатенации строк или при форматировании строк), и у него это не выходит, т.к. байтовая строка содержит не-ascii символы, а кодировка по умолчанию - ascii.

    В общем случае - выяснить, где падает и какую именно строку питон пытается преобразовать, откуда она берется.

    В частных случаях - при использовании django-debug-toolbar бывает такое, что вместо одного исключения другое выпадает - если django-debug-toolbar используется, попробуйте его отключить (убрав его middleware из настроек).

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

    Еще про частности - стоит проверить, что все методы __str__ и __repr__ возвращают байтовые строки, а все методы __unicode__ возвращают юникодные строки (применительно к джанге - особенно часто в моделях эти ошибки). Ну и что все verbose_name, названия полей и т.д. - юникодные (с буквой u спереди). Это все равно проверить не помешает - иначе поправите одну ошибку, где-то потом вылезет другая, во 2м питоне лучший способ не иметь проблем с юникодом - писать внимательно.

    И еще про частности - если просто информации о переменных недостаточно, и нужно глубже копать, можно поставить https://github.com/django-extensions/django-extensions, запустить ./manage.py runserver_plus и в каждом месте трейса более детально разобраться.

  4. Антон Силин

    10.08.2012

    0 ↑
    0 ↓
    Ну вот, например, вижу я в логах вот такой трейсбек (Во вьюхе у модели вместо count() написал countt()):
    Traceback (most recent call last):
    File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/wsgi.py", line 241, in __call__
    response = self.get_response(request)
    File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py", line 179, in get_response
    response = self.handle_uncaught_exception(request, resolver, sys.exc_info())
    File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py", line 221, in handle_uncaught_exception
    return debug.technical_500_response(request, *exc_info)
    File "/usr/local/lib/python2.7/dist-packages/django/views/debug.py", line 66, in technical_500_response
    html = reporter.get_traceback_html()
    File "/usr/local/lib/python2.7/dist-packages/django/views/debug.py", line 286, in get_traceback_html
    c = Context(self.get_traceback_data())
    File "/usr/local/lib/python2.7/dist-packages/django/views/debug.py", line 244, in get_traceback_data
    frames = self.get_traceback_frames()
    File "/usr/local/lib/python2.7/dist-packages/django/views/debug.py", line 398, in get_traceback_frames
    'vars': self.filter.get_traceback_frame_variables(self.request, tb.tb_frame),
    File "/usr/local/lib/python2.7/dist-packages/django/views/debug.py", line 194, in get_traceback_frame_variables
    value = self.get_request_repr(value)
    File "/usr/local/lib/python2.7/dist-packages/django/views/debug.py", line 102, in get_request_repr
    return build_request_repr(request, POST_override=self.get_post_parameters(request))
    File "/usr/local/lib/python2.7/dist-packages/django/http/__init__.py", line 179, in build_request_repr
    unicode(meta)))
    UnicodeDecodeError: 'ascii' codec can't decode byte 0xd0 in position 1826: ordinal not in range(128)
    И где искать ошибку? В "get_request_repr" что ему не понравилось в запросе, если он просто "http://site.ru/job/"?
  5. Антон Силин

    10.08.2012

    0 ↑
    0 ↓
    Все модели описаны примерно так:
    class Vacancy(models.Model):

    name = models.CharField(u'Вакансия', max_length = 256)

    def __unicode__(self):
    return self.name

    class Meta:
    ordering = ['-date']
    Может, я тут что-то забыл? Мне кажется, трейсбеки в джанго слишком неподробные(
  6. смотрим код

    Явно что-то из GET, POST, cookies или meta приехало в неконвертируемом в юникод виде. Смотрите, что туда пришло, и разбирайтесь, откуда.

  7. Антон Силин

    11.08.2012

    0 ↑
    0 ↓
    Да, спасибо, проблема была именно в meta - я туда засовываю orm-объект Region() с информацией о текущем регионе. Именно его не может нормально показать страница ошибки django.

    Убрал __unicode__() из описания этого класса - и все заработало!

    Однако, остался вопрос: как же мне правильно описать метод __unicode__ в своей модели (можно его и просто убрать - не смертельно, но хотелось бы оставить)?

    Пробовал два варианта, оба не помогли:

    def __unicode__(self):
    #return self.name
    return u'%s' % self.name
  8. KaBELSEA

    11.08.2012

    0 ↑
    0 ↓
    В таком случае скажите в какой кодировке у вас таблицы, и в частности поле name.
  9. И посмотрите, что в консоли выводит print unicode(Region())

  10. Антон Силин

    11.08.2012

    0 ↑
    0 ↓
    Кодировка у поля - utf8_general_ci

    В консоли:
    >>> r = Region.objects.all()[0]
    >>> r
    <Region: Абакан>
    >>> print unicode(r)
    Абакан

    Вроде все в порядке, а UnicodeDecodeError продолжает мозолить глаза, если __unicode__ описан.
  11. KaBELSEA

    11.08.2012

    0 ↑
    0 ↓
    # -*- coding: utf-8 -*-

    ???
  12. Покрутите еще __repr__. Что-то мне подсказывает, что при сворачивании меты в строку для ее составных частей не совсем __unicode__ юзается.

  13. Антон Силин

    12.08.2012

    0 ↑
    0 ↓
    # -*- coding: utf-8 -*- - везде есть, про эту кракозябру я не забываю.

    __repr__ прикручивал в таких же двух вариантах, как и __unicode__ - такая же ошибка.
  14. Мне кажется, что __repr__ как раз должен быть неюникодным

  15. Mikhail Korobov

    12.08.2012

    0 ↑
    0 ↓

    __repr__ должен возвращать байтовую строку; self.name и u'%s' % self.name возвратят юникодную в случае django-моделей (если .name - это поле).

    С __repr__ во втором питоне вообще беда - он должен возвращать байтовую строку в неизвестно какой кодировке (т.к. может использоваться в консоли - там нужна кодировка консоли; может - в логе, там нужно кодировка лога - utf8, скорее всего, ну и т.д.) Поэтому я не знаю, как в __repr__ использовать не-ascii символы без кодирования их, например, кодировкой unicode-escape (чтоб получалась строка, в которой нет не-ascii символов).

    Конкретно эта ошибка, как мне кажется - баг в джанге. __repr__ у моделей возвращает строку в utf8, в build_request_repr вызывается pformat(meta), который в случае с моделями вернет тоже строку в utf8 и не-ascii символами, дальше unicode(pformat(meta)) упадет, т.к. попробует эту строку деводировать с кодировкой ascii.

    Но не суть - зачем в request.META засовывать orm-объекты? Не нужно так делать :) request.META - это словарь с http-заголовками. Если уж так хочется что-то через объект request передавать, создайте свой атрибут.

  16. Mikhail Korobov

    12.08.2012

    0 ↑
    0 ↓

    Хотел баг в трекере завести, но непонятно, как его описать-то. "Ничего не работает, если засунуть экземпляр модели в request.META" - так не надо засовывать экземпляр модели в request.META, там должны быть http-заголовки и их значения-юникодные строки (с которыми все работает, втч если они не ascii, т.к. repr(unicode_string) возвращает байтовую строку в кодировке unicode-escape под 2.х), и вообще на запись туда лезть не нужно, это вполне технический словарь с информацией о http-заголовках, переданных браузером (или прокси).

  17. Slay

    15.08.2012

    1 ↑
    0 ↓
    Есть одна простенькая библиотека, которая способна решить эту проблему.

    http://pypi.python.org/pypi/Unidecode/

    Нужно переопределить __repr__ примерно так:
    from unidecode import unidecode

    def __repr__(self):
    return unidecode(self.name)
    На выходе будет ascii строка.
  18. KaBELSEA

    15.08.2012

    0 ↑
    0 ↓
    А по-моему незачем размещать объекты orm в местах для этого не предназначенных)
  19. Mikhail Korobov

    20.08.2012

    0 ↑
    0 ↓

    кстати, не-ascii __repr__ у джанго-моделей поправили в транке, https://github.com/django/django/commit/3fce0d2a9162cf6e749a6de0b18890dea8955e89

    @Slay - хорошая библиотечка; найти бы у нее какой-нибудь не-GPL аналог.

Внимание! Это довольно старый топик, посты в него не попадут в новые, и их никто не увидит. Пишите пост, если хотите просто дополнить топик, а чтобы задать новый вопрос — начните новый.