Данные Юникода
Django поддерживает данные Юникода везде.
Этот документ расскажет вам, что вам нужно знать, если вы пишете приложения, которые используют данные или шаблоны, закодированные не в 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принимает любое значение, которое принимается функциейstr()Python для обработки ошибок. -
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, который может содержать символы Юникода. Используйте эти функции для цитирования и преобразования IRI в URI:
- Функция
django.utils.encoding.iri_to_uri(), которая реализует преобразование из IRI в URI, как требуется RFC 3987 Раздел 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 Раздел 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.
Файлы
Если вы намерены разрешить пользователям загружать файлы, вы должны убедиться, что среда, используемая для запуска 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.2/ref/unicode/