Данные Unicode
Django поддерживает данные Unicode повсюду.
В этом документе рассказывается, что нужно знать при разработке приложений, использующих данные или шаблоны, закодированные не в ASCII.
Создание базы данных
Убедитесь, что база данных настроена для хранения произвольных строковых данных. Обычно для этого нужно использовать кодировку UTF-8 или UTF-16. Если вы используете более ограниченную кодировку — например, latin1 (iso8859-1), — вы не сможете хранить в базе данных некоторые символы, и информация будет потеряна.
- Пользователям MySQL следует обратиться к руководству MySQL, чтобы узнать, как задать или изменить кодировку набора символов базы данных.
- Пользователям PostgreSQL следует обратиться к руководству PostgreSQL, чтобы узнать, как создавать базы данных с правильной кодировкой.
- Пользователям Oracle следует обратиться к руководству Oracle, чтобы узнать, как задать (раздел 2) или изменить (раздел 11) кодировку набора символов базы данных.
- Пользователям SQLite ничего делать не нужно. SQLite всегда использует UTF-8 для внутренней кодировки.
Все серверные части баз данных Django автоматически преобразуют строки в подходящую кодировку для взаимодействия с базой данных. Они также автоматически преобразуют строки, полученные из базы данных, в строки. Вам даже не нужно сообщать Django, какую кодировку использует ваша база данных: это обрабатывается прозрачно.
Подробнее см. в разделе «API базы данных» ниже.
Общая обработка строк
При использовании строк в Django — например, в запросах к базе данных, при рендеринге шаблонов или где-либо ещё — у вас есть два варианта кодировки этих строк. Вы можете использовать обычные строки или байтовые строки (начинающиеся с «b»).
Предупреждение
Байтовая строка не содержит информации о своей кодировке. Поэтому нам приходится делать предположение, и Django предполагает, что все байтовые строки закодированы в UTF-8.
Если передать Django строку, закодированную в другом формате, могут возникнуть весьма необычные проблемы. Обычно в какой-то момент Django вызовет UnicodeDecodeError.
Если ваш код использует только данные ASCII, можно безопасно использовать обычные строки и передавать их как угодно, поскольку ASCII является подмножеством UTF-8.
Не думайте, что если для параметра DEFAULT_CHARSET задано значение, отличное от 'utf-8', то в байтовых строках можно использовать эту другую кодировку! DEFAULT_CHARSET применяется только к строкам, созданным в результате рендеринга шаблонов (и электронной почты). Для внутренних байтовых строк Django всегда предполагает кодировку UTF-8. Причина в том, что параметр DEFAULT_CHARSET фактически не находится под вашим контролем (если вы разработчик приложения). Им управляет человек, устанавливающий и использующий ваше приложение. Если этот человек выберет другое значение, ваш код всё равно должен продолжать работать. Следовательно, код не может полагаться на этот параметр.
В большинстве случаев при работе со строками Django преобразует их в строки прежде, чем выполнять какие-либо действия. Поэтому, как правило, если вы передаёте байтовую строку, будьте готовы получить в результате обычную строку.
Переведённые строки
Помимо строк и байтовых строк, при использовании Django вы можете встретить третий тип строкоподобных объектов. Средства интернационализации фреймворка вводят понятие «ленивого перевода» — строки, помеченной как переведённая, но фактический результат перевода которой определяется лишь при использовании объекта в строке. Эта возможность полезна в случаях, когда локаль перевода неизвестна до момента использования строки, хотя сама строка могла быть создана при первоначальном импорте кода.
Обычно вам не придётся беспокоиться о ленивых переводах. Просто имейте в виду: если при проверке объекта выяснится, что это объект django.utils.functional.__proxy__, значит, перед вами ленивый перевод. Вызов str() с ленивым переводом в качестве аргумента создаст строку на текущем языке.
Подробнее об объектах ленивого перевода см. в документации по интернационализации.
Полезные вспомогательные функции
Поскольку одни и те же операции со строками выполняются снова и снова, Django предоставляет несколько полезных функций, которые упрощают работу со строками и байтовыми строками.
Функции преобразования
Модуль django.utils.encoding содержит несколько удобных функций для преобразования строк в байтовые строки и обратно.
-
smart_str(s, encoding='utf-8', strings_only=False, errors='strict')преобразует входное значение в строку. Параметрencodingзадаёт кодировку входных данных. (Например, Django использует эту функцию внутри при обработке данных формы, которые могут быть закодированы не в UTF-8.) Если параметруstrings_onlyприсвоить значение True, числа Python, логические значения иNoneне будут преобразованы в строку (они сохранят исходные типы). Параметрerrorsпринимает любые значения, допустимые для обработки ошибок в функции Pythonstr(). -
force_str(s, encoding='utf-8', strings_only=False, errors='strict')почти во всех случаях идентичнаsmart_str(). Разница проявляется, когда первый аргумент является экземпляром ленивого перевода. Еслиsmart_str()сохраняет ленивые переводы, тоforce_str()принудительно преобразует эти объекты в строку (что приводит к выполнению перевода). Обычно следует использоватьsmart_str(). Однакоforce_str()полезна в тегах и фильтрах шаблонов, которым обязательно нужна строка для работы, а не просто объект, который можно преобразовать в строку. -
smart_bytes(s, encoding='utf-8', strings_only=False, errors='strict')по сути является противоположностьюsmart_str(). Она принудительно преобразует первый аргумент в байтовую строку. Параметрstrings_onlyведёт себя так же, как вsmart_str()иforce_str(). Это немного отличается по семантике от встроенной функции Pythonstr(), но такое отличие необходимо в нескольких местах внутреннего кода Django.
Обычно вам понадобится только force_str(). Вызывайте её как можно раньше для любых входных данных, которые могут быть как строкой, так и байтовой строкой, и после этого считайте результат обычной строкой.
Обработка URI и IRI
Веб-фреймворкам приходится работать с URL (которые являются разновидностью IRI). Одно из требований к URL — использование только символов ASCII. Однако в международной среде может потребоваться создать URL из IRI — если говорить очень упрощённо, URI, который может содержать символы Unicode. Используйте следующие функции для экранирования и преобразования IRI в URI:
- Функция
django.utils.encoding.iri_to_uri(), которая преобразует IRI в URI в соответствии с требованиями раздела 3.1 RFC 3987. - Функции
urllib.parse.quote()иurllib.parse.quote_plus()из стандартной библиотеки Python.
У этих двух групп функций немного разные задачи, и важно не путать их. Обычно quote() применяют к отдельным частям пути IRI или URI, чтобы зарезервированные символы, такие как «&» или «%», были правильно закодированы. Затем применяют iri_to_uri() ко всему IRI, и она преобразует все символы, не входящие в ASCII, в соответствующие кодированные значения.
Примечание
Строго говоря, неверно утверждать, что iri_to_uri() реализует полный алгоритм из спецификации IRI. Она этого не делает (пока не выполняет часть алгоритма, отвечающую за кодирование доменных имён в международных доменах).
Функция iri_to_uri() не изменяет символы ASCII, которые в остальном допустимы в URL. Так, например, символ «%» не кодируется повторно при передаче в iri_to_uri(). Это означает, что функции можно передать URL целиком — она не повредит строку запроса или другие его части.
Пример поможет прояснить ситуацию:
>>> from urllib.parse import quote
>>> from django.utils.encoding import iri_to_uri
>>> quote("Paris & Orléans")
'Paris%20%26%20Orl%C3%A9ans'
>>> iri_to_uri("/favorites/François/%s" % quote("Paris & Orléans"))
'/favorites/Fran%C3%A7ois/Paris%20%26%20Orl%C3%A9ans'
Если внимательно посмотреть, можно заметить, что часть, созданная с помощью quote() во втором примере, не была повторно экранирована при передаче в iri_to_uri(). Это очень важная и полезная особенность. Благодаря ей можно создавать IRI, не беспокоясь о наличии в нём символов, не входящих в ASCII, а затем в самом конце вызвать iri_to_uri() для полученного результата.
Кроме того, Django предоставляет функцию django.utils.encoding.uri_to_iri(), которая преобразует URI в IRI в соответствии с разделом 3.2 RFC 3987.
Пример:
>>> from django.utils.encoding import uri_to_iri
>>> uri_to_iri("/%E2%99%A5%E2%99%A5/?utf8=%E2%9C%93")
'/♥♥/?utf8=✓'
>>> uri_to_iri("%A9hello%3Fworld")
'%A9hello%3Fworld'
В первом примере символы UTF-8 не экранируются. Во втором примере процентные кодировки остаются без изменений, поскольку они находятся вне допустимого диапазона UTF-8 или представляют зарезервированный символ.
Функции iri_to_uri() и uri_to_iri() идемпотентны, то есть всегда выполняется следующее:
iri_to_uri(iri_to_uri(some_string)) == iri_to_uri(some_string) uri_to_iri(uri_to_iri(some_string)) == uri_to_iri(some_string)
Поэтому одну и ту же функцию можно безопасно вызывать несколько раз для одного URI/IRI, не опасаясь повторного экранирования.
Модели
Поскольку все строки возвращаются из базы данных как объекты str, поля моделей, основанные на строках (CharField, TextField, URLField и т. д.), при получении данных из базы данных Django содержат значения Unicode. Это всегда так, даже если данные могли бы поместиться в байтовую строку ASCII.
При создании модели или заполнении поля можно передавать байтовые строки, и Django преобразует их в обычные строки, когда это потребуется.
Будьте осторожны в get_absolute_url()
URL могут содержать только символы ASCII. Если вы создаёте URL из частей данных, которые могут содержать символы, не входящие в ASCII, позаботьтесь о том, чтобы правильно закодировать результат для использования в URL. Функция reverse() делает это автоматически.
Если вы создаёте URL вручную (то есть не используя функцию reverse()), вам нужно позаботиться о кодировании самостоятельно. В этом случае используйте функции iri_to_uri() и quote(), описанные выше. Например:
from urllib.parse import quote
from django.utils.encoding import iri_to_uri
def get_absolute_url(self):
url = "/person/%s/?x=0&y=0" % quote(self.location)
return iri_to_uri(url)
Эта функция возвращает правильно закодированный URL, даже если self.location имеет значение вроде «Jack visited Paris & Orléans». (На самом деле вызов iri_to_uri() в приведённом выше примере не обязателен, поскольку все символы, не входящие в ASCII, были бы удалены при экранировании в первой строке.)
Шаблоны
При создании шаблонов вручную используйте строки:
from django.template import Template
t2 = Template("This is a string template.")
Однако чаще всего шаблоны читаются из файловой системы. Если файлы шаблонов сохранены не в кодировке UTF-8, измените параметр TEMPLATES. Встроенная серверная часть django предоставляет параметр 'file_charset' для изменения кодировки, используемой при чтении файлов с диска.
Параметр DEFAULT_CHARSET задаёт кодировку отрендеренных шаблонов. По умолчанию используется UTF-8.
Файлы
Если вы собираетесь разрешить пользователям загружать файлы, необходимо убедиться, что среда, в которой запускается Django, настроена для работы с именами файлов, содержащими символы, не входящие в ASCII. Если среда настроена неправильно, при сохранении файлов с именами или содержимым, содержащими такие символы, возникнут исключения UnicodeEncodeError.
Поддержка имён файлов в UTF-8 в файловых системах различается и может зависеть от среды. Проверьте текущую конфигурацию в интерактивной оболочке Python, выполнив:
import sys sys.getfilesystemencoding()
В результате должно появиться «UTF-8».
За установку ожидаемой кодировки в Unix отвечает переменная окружения LANG. Чтобы узнать подходящий синтаксис и место для задания этой переменной, обратитесь к документации своей операционной системы и сервера приложений. Примеры см. в документе Как использовать Django с Apache и mod_wsgi.
В среде разработки может потребоваться добавить настройку в файл ~.bashrc, например:
export LANG="en_US.UTF-8"
Отправка форм
Отправка HTML-форм — непростой вопрос. Нет гарантии, что при отправке будут переданы сведения о кодировке, а значит, фреймворку может потребоваться угадать кодировку отправленных данных.
Django использует «ленивый» подход к декодированию данных формы. Данные объекта HttpRequest декодируются только при обращении к ним. Более того, большая часть данных вообще не декодируется. Декодирование применяется только к структурам данных HttpRequest.GET и HttpRequest.POST. Эти два поля возвращают свои элементы как данные Unicode. Все остальные атрибуты и методы HttpRequest возвращают данные в точности в том виде, в каком они были отправлены клиентом.
По умолчанию параметр DEFAULT_CHARSET используется в качестве предполагаемой кодировки данных формы. Чтобы изменить её для конкретной формы, можно задать атрибут encoding экземпляра HttpRequest. Например:
def some_view(request):
# We know that the data must be encoded as KOI8-R (for some reason).
request.encoding = "koi8-r"
...
Вы можете изменить кодировку даже после обращения к request.GET или request.POST; при всех последующих обращениях будет использоваться новая кодировка.
Большинству разработчиков не придётся менять кодировку формы, но эта возможность полезна для приложений, взаимодействующих с устаревшими системами, кодировку которых вы не можете контролировать.
Django не декодирует данные загруженных файлов, поскольку обычно они рассматриваются как наборы байтов, а не как строки. Автоматическое декодирование изменило бы смысл потока байтов.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/6.0/ref/unicode/