Данные 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принимает любое значение, которое принимается функцией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, который может содержать символы 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/4.0/ref/unicode/