Spec-Zone.ru › Django 2.0

Данные 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 символы были удалены при цитировании в первой строке.)

API базы данных

Вы можете передавать строки или UTF-8 байтовые строки в качестве аргументов к методам filter() и т.п. в API базы данных. Следующие два набора запросов идентичны:

qs = People.objects.filter(name__contains='Å')
qs = People.objects.filter(name__contains=b'\xc3\x85') # UTF-8 encoding of Å

Шаблоны

При ручном создании шаблонов можно использовать строки или UTF-8 байтовые строки:

from django.template import Template
t1 = Template(b'This is a bytestring 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.0/ref/unicode/

Spec-Zone.ru

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