Spec-Zone.ru › Django 6.0

Данные 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 принимает любые значения, допустимые для обработки ошибок в функции Python str().
  • 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(). Это немного отличается по семантике от встроенной функции Python str(), но такое отличие необходимо в нескольких местах внутреннего кода Django.

Обычно вам понадобится только force_str(). Вызывайте её как можно раньше для любых входных данных, которые могут быть как строкой, так и байтовой строкой, и после этого считайте результат обычной строкой.

Обработка URI и IRI

Веб-фреймворкам приходится работать с URL (которые являются разновидностью IRI). Одно из требований к URL — использование только символов ASCII. Однако в международной среде может потребоваться создать URL из IRI — если говорить очень упрощённо, URI, который может содержать символы Unicode. Используйте следующие функции для экранирования и преобразования IRI в URI:

  • Функция django.utils.encoding.iri_to_uri(), которая преобразует IRI в URI в соответствии с требованиями раздела 3.1 RFC 3987.
  • Функции 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 в соответствии с разделом 3.2 RFC 3987.

Пример:

>>> 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 и т. д.), при получении данных из базы данных Django содержат значения Unicode. Это всегда так, даже если данные могли бы поместиться в байтовую строку 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. Если среда настроена неправильно, при сохранении файлов с именами или содержимым, содержащими такие символы, возникнут исключения UnicodeEncodeError.

Поддержка имён файлов в UTF-8 в файловых системах различается и может зависеть от среды. Проверьте текущую конфигурацию в интерактивной оболочке Python, выполнив:

import sys

sys.getfilesystemencoding()

В результате должно появиться «UTF-8».

За установку ожидаемой кодировки в Unix отвечает переменная окружения LANG. Чтобы узнать подходящий синтаксис и место для задания этой переменной, обратитесь к документации своей операционной системы и сервера приложений. Примеры см. в документе Как использовать 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/6.0/ref/unicode/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API