Данные 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.1/ref/unicode/