Данные 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 — это что-то вроде «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.
Теги и фильтры шаблонов
- Всегда возвращайте строки из метода
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.2/ref/unicode/