Данные 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 в соответствии с требованиями RFC 3987#section-3.1. - Функции
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 в соответствии с RFC 3987#section-3.2.
Пример для демонстрации:
>>> 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 и т. д.), будут содержать значения Unicode, когда Django извлекает данные из базы данных. Это всегда так, даже если данные могут уместиться в байтовой строке 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 — это что-то вроде «Джек посетил Париж и Орлеан». (На самом деле, вызов 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.
Тэги и фильтры шаблонов
- Всегда возвращайте строки из метода
render()тэга шаблона и из фильтров шаблонов. - Используйте
force_str()вместоsmart_str()в этих местах. рендеринг тэгов и вызовы фильтров происходят по мере рендеринга шаблона, поэтому нет преимуществ в отсрочке преобразования ленивых объектов перевода в строки. В этом случае проще работать только со строками.
Файлы
Если вы планируете разрешать пользователям загружать файлы, необходимо убедиться, что среда, используемая для запуска Django, настроена для работы с именами файлов, содержащими не-ASCII символы. Если ваша среда не настроена правильно, при сохранении файлов с именами файлов или содержимым, содержащим не-ASCII символы, возникнут исключения UnicodeEncodeError.
Поддержка файловой системы для имён файлов UTF-8 варьируется и может зависеть от среды. Проверьте текущую конфигурацию в интерактивной оболочке Python, выполнив:
import sys sys.getfilesystemencoding()
Это должно вывести «UTF-8».
Переменная среды LANG отвечает за установку ожидаемой кодировки на платформах Unix. Обратитесь к документации вашей операционной системы и сервера приложений для получения правильного синтаксиса и места для установки этой переменной. Смотрите Как использовать 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/5.1/ref/unicode/