Форум сайта python.su
Гляньте сюда: https://bitbucket.org/kmike/django-assist-ru/src/9aa1bdb4cc6e/assist/decorators.py
строчку с “response.content = response.content.replace(UTF8_HEADER, CP1251_HEADER, 1)” можно выкинуть, если Content-Type в шаблоне правильный.
Джанга ожидает юникод везде во “внутренностях” (втч при рендеринге шаблонов), и это замечательно. Кодировка имеет значение при общении с внешним миром - поэтому кодировать лучше все на выходе, как можно позже.
Еще хорошо бы завязать с методом тыка при работе с кодировками - лучше один раз разобраться по-нормальному, там ничего сложного же нет. Советы в духе “попробуйте ‘это_проверка’.encode('cp1251')” - это ужасно.
“open(root_dir+'/templates/test.html').read()” - так тоже не делайте, пожалуйста - опять байтовые строки вместо юникода, не закрываете файл, не используете более простые джанговские средства почему-то.
Офлайн
kmikeя с удовольствием выслушаю ваше универсальное предложение по работе с кодировками (а то я бился, бился и у меня ничего не вышло), создайте тему в разделе флейм и там обсудим с другими участниками форума эту проблему. В противном случае, это всего лишь голословное заявление=)
учше один раз разобраться по-нормальному, там ничего сложного же нет.
kmikeВы не правы. Проверьте сами (lsof для Linux или , например, Unlocker для windows)
не закрываете файл
kmikeЭто вы об django.template.loader.get_template() ? Ну там нчиег осверхординарного нет: file.read().decode(settings.FILE_CHARSET), но тем не менее замечание ваше принимаю, так как оно имеет смысл в целом,но не решающее в данном вопросе.
не используете более простые джанговские средства почему-то.
Отредактировано (Март 23, 2012 03:30:03)
Офлайн
В смысле, “лучше разобраться по-нормальному” - это голословное утверждение?) Универсальное решение при работе с кодировками одно - это четко понимать, что такое кодировка, что такое байты и что такое юникод :)
Проблемы с кодировками вылезают оттого, что во 2м питоне в объектах класса str хранятся “голые” байты. Когда такие переменные используются для хранения строк текста, то получается, что прямо в самой переменной нет никакой информации о том, в какой кодировке строка (== по какому алгоритму была получена последовательность байт для данного текста). Восстановить текст из последовательности байт можно разными способами (==с разными кодировками), а т.к. у питона никакой информации о кодировке нет, то он выбирает черти какую кодировку (обычно это оказывается ascii) и пробует с ней декодировать текст - иногда это работает, иногда падает. Собственно, главное отличие 3го питона от 2го по части юникода в том, что 3й НЕ пробует декодировать строку с черти какой кодировкой, а просто всегда падает с исключением (а не иногда, как 2й).
Отсюда 2 следствия - вы всегда должны знать, в какой кодировке к вам поступают байтовые строки, и перед операциями с ними как с текстом декодировать их в питоний unicode. unicode - это такой тип, переменные которого гарантированно могут хранить все буквы текста (как это делается внутри - считается, что не наша забота), эдакая абстрацкия “текст”.
Если б не существовало “внешнего мира”, все строки были бы в питоне юникодными и не было бы никаких проблем с кодировками. Но “внешний мир” существует, и поэтому нужно декодировать байты, полученные из внешнего мира (например, из файла) и кодировать unicode в какую-то кодировку (== преобразовывать в последовательность байт по какому-то алгоритму, например, utf8 или cp1251), чтоб внешний мир мог данные принять обратно (например, при записи в файл или отдаче ответа на веб-запрос).
Джанга многое из этого делает автоматически (запрос она декодирует в unicode, ответ кодирует в кодировку), так что “внутри” у нас везде юникод и никаких проблем. В большинстве случаев это работает отлично, но когда часть страниц нужно отдавать в одной кодировке, а часть в другой, то приходится перекодировать ответ “руками” - например, так, как по ссылке в примере.
Если нужно запрос декодировать из другой кодировки, то у request есть свойство encoding, которое можно во вьюхе установить в нужное значение, и тогда GET, POST из запроса будут декодированы в юникод по нужному алгоритму (== как текст в данной кодировке).
Про файл и open(). Я же написал “вы не закрываете файл”, а не “файл не закрывается”.
open(path).read() закрывает файл только из-за особенностей реализации GC в CPython, в pypy и других реализациях файл не закроется - imho лучше приучиться писать все явно: with codecs.open(path, ‘rt’, 'utf8) as f:
Отредактировано (Март 23, 2012 09:49:22)
Офлайн
kmike
Вот вы хорошо пишите:)похвально. Только все это я знаю, за исключением особенностей реализации.
Вот как раз проблема и всплывает в том что в реальной жизни далеко не всегда можно заранее знать в какой кодировке придет строка, и реальное приложение остановит свою работу при возникновении в случайном месте UnicodeEncode/Decode exception. Я думал может быть вы в подобной ситуации знаете выход, как видите просто понимание строк и юникода здесь не выход. Я как то писал некую функцию, которую нужно было применять к любой строке которая попадает в программу извне, предназначена она была вернуть корректно сформированный объект юникода. Но что то там все равно не срасталось.
kmikeНу вот тут как бы неоднозначно вас понять можно, лучше было бы перефразировать, я бы вас сразу понял как надо.
Про файл и open(). Я же написал “вы не закрываете файл”, а не “файл не закрывается”.
Офлайн
Чтоб UnicodeEncode/Decode exception возникал не в случайном месте, а в строго определенном, нужно декодировать строку в юникод как можно раньше, и кодировать в кодировку как можно позже, а с байтовыми строками не работать вовсе, вот я о чем)
Если неизвестно, в какой кодировке текст приходит извне (или какую кодировку внешний мир хочет), то тут нужно не питоний код писать, а выяснять, в какой кодировке он приходит (или какую кодировку хочет).
По конкретной проблеме - вы же правильно уже все сделали - установили, что ftp-сервер хочет cp1251, и придумали, как заставить браузер передать 1251 - вывести ссылку на странице с 1251; осталось страничку в 1251 вернуть. Про “лучше 1 раз разобраться” я не вам писал, а автору ‘это_проверка’.encode('cp1251')
Кстати, вот так еще можно вроде:
response = HttpResponse(rendered_template.encode('cp1251'))
response._charset = ‘cp1251’
return response
Офлайн
kmike
Спасибо, проблема решена. решение оформлено в первом посте.
Офлайн