Spec-Zone.ru › Django 2.2

Данные Юникода

Django поддерживает данные Юникода во всех частях.

Этот документ расскажет вам, что вам нужно знать, если вы пишете приложения, которые используют данные или шаблоны, закодированные не в ASCII.

Создание базы данных

Убедитесь, что ваша база данных настроена для хранения произвольных строковых данных. Обычно это означает задание кодировки UTF-8 или UTF-16. Если вы используете более ограничительную кодировку, например, latin1 (iso8859-1), вы не сможете сохранить определенные символы в базе данных, и информация будет потеряна.

  • Пользователи MySQL, обратитесь к руководству MySQL за подробностями о том, как установить или изменить кодировку набора символов базы данных.
  • Пользователи PostgreSQL, обратитесь к руководству PostgreSQL (раздел 22.3.2 в PostgreSQL 9) за подробностями о создании баз данных с правильной кодировкой.
  • Пользователи 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_text(s, encoding='utf-8', strings_only=False, errors='strict') преобразует свой вход в строку. Параметр encoding указывает кодировку ввода. (Например, Django использует это внутренне при обработке данных ввода формы, которые могут быть не закодированы в UTF-8.) Параметр strings_only, если установлен в True, приведет к тому, что числа Python, булевы значения и None не будут преобразованы в строку (они сохранят свои исходные типы). Параметр errors принимает любые значения, которые принимаются функцией Python str() для обработки ошибок.
  • force_text(s, encoding='utf-8', strings_only=False, errors='strict') идентична smart_text() почти во всех случаях. Разница заключается в том, когда первый аргумент — экземпляр ленивого перевода. В то время как smart_text() сохраняет ленивые переводы, force_text() форматирует эти объекты в строку (вызывая перевод). Обычно вы захотите использовать smart_text(). Однако force_text() полезна в тегах и фильтрах шаблонов, которые обязательно должны работать со строкой, а не с чем-то, что можно преобразовать в строку.
  • smart_bytes(s, encoding='utf-8', strings_only=False, errors='strict') по существу является противоположностью smart_text(). Она форматирует первый аргумент в байтовую строку. Параметр strings_only имеет то же поведение, что и для smart_text() и force_text(). Это немного отличается от семантики встроенной функции Python str(), но в некоторых местах внутренней части Django необходима эта разница.

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

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

Веб-фреймворки должны обрабатывать URL-адреса (которые являются типом IRI). Одно из требований к URL-адресам заключается в том, что они закодированы только с использованием ASCII-символов. Однако в международной среде вам может потребоваться создать URL-адрес из IRI — очень грубо говоря, URI, который может содержать символы Юникода. Используйте эти функции для цитирования и преобразования 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_text() вместо smart_text() в этих местах. Рендеринг тэгов и вызовы фильтров происходят по мере рендеринга шаблона, поэтому нет преимуществ в отсрочке преобразования объектов ленивого перевода в строки. В этот момент проще работать только со строками.

Файлы

Если вы планируете разрешить пользователям загружать файлы, вы должны убедиться, что среда, в которой выполняется Django, настроена для работы с именами файлов, не содержащими символы ASCII. Если ваша среда не настроена должным образом, при сохранении файлов с именами файлов, содержащими символы, не являющиеся ASCII, вы столкнетесь с исключениями UnicodeEncodeError.

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

import sys
sys.getfilesystemencoding()

Это должно вывести «UTF-8».

Переменная среды LANG отвечает за установку ожидаемой кодировки на платформах Unix. Обратитесь к документации вашей операционной системы и веб-сервера для получения соответствующего синтаксиса и местоположения для установки этой переменной.

В вашей среде разработки, возможно, потребуется добавить настройку в ваш ~.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/2.2/ref/unicode/

Spec-Zone.ru

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