Spec-Zone.ru › Django 2.1

Данные Unicode

Django изначально поддерживает данные Unicode везде. Если ваша база данных каким-либо образом может хранить данные, вы можете безопасно передавать строки в шаблоны, модели и базу данных.

Этот документ расскажет вам, что вам нужно знать, если вы пишете приложения, использующие данные или шаблоны, закодированные в чём-то другом, кроме 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, который может содержать символы 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. Если ваши файлы шаблонов не хранятся в кодировке UTF-8, установите значение настройки FILE_CHARSET на кодировку файлов на диске. Когда Django считывает файл шаблона, он преобразует данные из этой кодировки в Unicode. (FILE_CHARSET по умолчанию устанавливается в 'utf-8').

Настройка 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.1/ref/unicode/

Spec-Zone.ru

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