Spec-Zone.ru › Django 1.8

Данные Unicode

Django изначально поддерживает данные Unicode во всех местах. Если ваша база данных каким-то образом может хранить данные, вы можете безопасно передавать строки 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 автоматически преобразуют строки Unicode в соответствующую кодировку для работы с базой данных. Они также автоматически преобразуют строки, полученные из базы данных, в строки Python Unicode. Вам даже не нужно сообщать Django, какую кодировку использует ваша база данных: это обрабатывается прозрачно.

Для получения дополнительной информации см. раздел «API базы данных» ниже.

Общая обработка строк

Всякий раз, когда вы используете строки с Django (например, в поиске по базе данных, рендеринге шаблонов или где-либо ещё), у вас есть два варианта кодировки этих строк. Вы можете использовать строки Unicode или обычные строки (иногда называемые «строками байтов»), которые закодированы с помощью UTF-8.

В Python 3 логика обратная, то есть обычные строки являются Unicode, а когда вы хотите создать конкретно строку байтов, вам нужно префикс строки «b». Как мы делаем в коде Django с версии 1.5, мы рекомендуем импортировать unicode_literals из библиотеки __future__ в вашем коде. Затем, когда вам нужно создать конкретно строку байтов, добавьте префикс 'b'.

Наследие Python 2:

my_string = "This is a bytestring"
my_unicode = u"This is an Unicode string"

Python 2 с литералами unicode или Python 3:

from __future__ import unicode_literals

my_string = b"This is a bytestring"
my_unicode = "This is an Unicode string"

См. также Совместимость с Python 3.

Предупреждение

Строка байтов не содержит информации о своей кодировке. По этой причине нам нужно сделать предположение, и Django предполагает, что все строки байтов находятся в UTF-8.

Если вы передадите строку в Django, закодированную в другом формате, произойдут интересные ошибки. Обычно Django в какой-то момент сгенерирует UnicodeDecodeError.

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

Не думайте, что если ваше значение DEFAULT_CHARSET установлено на значение отличное от 'utf-8', вы можете использовать эту другую кодировку в строках байтов! DEFAULT_CHARSET применяется только к строкам, сгенерированным в результате рендеринга шаблонов (и электронной почты). Django всегда будет использовать UTF-8 кодировку для внутренних строк байтов. Причина в том, что значение DEFAULT_CHARSET не находится под вашим непосредственным контролем (если вы разработчик приложения). Оно находится под контролем человека, устанавливающего и использующего ваше приложение – и если этот человек выберет другое значение, ваш код должен продолжать работать. Следовательно, он не может полагаться на это значение.

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

Переведенные строки

Помимо строк Unicode и строк байтов, существует третий тип строкоподобного объекта, который вы можете встретить при использовании Django. Функции интернационализации фреймворка вводят понятие «отложенного перевода» — строки, помеченной как переведенная, но чьи фактические результаты перевода не определяются до тех пор, пока объект не будет использован в строке. Эта функция полезна в тех случаях, когда языковой регион перевода неизвестен до тех пор, пока строка не будет использована, даже если строка была создана при первом импорте кода.

Обычно вам не нужно беспокоиться о ленивых переводах. Просто знайте, что если вы изучите объект, и он утверждает, что это объект django.utils.functional.__proxy__, это ленивый перевод. Вызов unicode() со строкой ленивого перевода в качестве аргумента сгенерирует строку Unicode в текущем языковом регионо.

Для получения дополнительной информации о ленивых объектах перевода см. документацию по интернационализации.

Полезные служебные функции

Поскольку некоторые операции со строками встречаются снова и снова, Django поставляет несколько полезных функций, которые должны облегчить работу с объектами Unicode и строками байтов.

Функции преобразования

Модуль django.utils.encoding содержит несколько функций, которые полезны для преобразования между Unicode и строками байтов.

  • smart_text(s, encoding='utf-8', strings_only=False, errors='strict') преобразует свой ввод в строку Unicode. Параметр encoding указывает кодировку ввода. (Например, Django использует это во внутренней обработке данных формы, которые могут быть закодированы не в UTF-8.) Параметр strings_only, если установлен в True, приведет к тому, что числа Python, булевы значения и None не будут преобразовываться в строку (они сохранят свои исходные типы). Параметр errors принимает любые значения, которые принимаются функцией Python unicode() для обработки ошибок.

    Если вы передаёте smart_text() объект, который имеет метод __unicode__, он будет использовать этот метод для преобразования.

  • force_text(s, encoding='utf-8', strings_only=False, errors='strict') идентична smart_text() практически во всех случаях. Разница заключается в том, когда первым аргументом является экземпляр отложенного перевода. В то время как smart_text() сохраняет отложенные переводы, force_text() принудительно преобразует эти объекты в строку Unicode (вызывая перевод). Обычно вы захотите использовать 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.

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

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

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

  • Функция django.utils.encoding.iri_to_uri() реализует преобразование IRI в URI в соответствии со спецификацией (RFC 3987#section-3.1).
  • Функции django.utils.http.urlquote() и django.utils.http.urlquote_plus() являются версиями стандартных функций Python urllib.quote() и urllib.quote_plus(), которые работают с символами, отличными от ASCII. (Данные преобразуются в UTF-8 перед кодированием.)

Эти две группы функций имеют немного разные цели, и важно их различать. Обычно вы бы использовали urlquote() для отдельных частей пути IRI или URI, чтобы любые зарезервированные символы, такие как '&' или '%', были правильно закодированы. Затем вы применяете iri_to_uri() к полному IRI, и он преобразует любые символы, не являющиеся ASCII, в правильные закодированные значения.

Примечание

Технически неверно утверждать, что iri_to_uri() реализует весь алгоритм в спецификации IRI. Он этого не делает (пока) не выполняет часть алгоритма кодирования международных доменных имён.

Функция iri_to_uri() не будет изменять символы ASCII, которые допускаются в URL. Таким образом, например, символ '%' не будет дополнительно кодироваться при передаче в iri_to_uri(). Это означает, что вы можете передать полный URL в эту функцию, и она не испортит строку запроса или что-либо подобное.

Пример может прояснить ситуацию:

>>> urlquote('Paris & Orléans')
'Paris%20%26%20Orl%C3%A9ans'
>>> iri_to_uri('/favorites/François/%s' % urlquote('Paris & Orléans'))
'/favorites/Fran%C3%A7ois/Paris%20%26%20Orl%C3%A9ans'

Если вы внимательно посмотрите, вы можете увидеть, что часть, сгенерированная urlquote() во втором примере, не была заключена в двойные кавычки при передаче в iri_to_uri(). Это очень важная и полезная функция. Это означает, что вы можете создать свой IRI, не беспокоясь о том, содержит ли он символы, не являющиеся ASCII, и затем, в самом конце, вызвать iri_to_uri() на результате.

Аналогично, Django предоставляет django.utils.encoding.uri_to_iri(), которая реализует преобразование из URI в IRI в соответствии с RFC 3987#section-3.2. Она декодирует все проценты кодировок, за исключением тех, которые не представляют собой корректную последовательность UTF-8.

Пример для демонстрации:

>>> uri_to_iri('/%E2%99%A5%E2%99%A5/?utf8=%E2%9C%93')
'/♥♥/?utf8=✓'
>>> uri_to_iri('%A9helloworld')
'%A9helloworld'

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

Модели

Поскольку все строки возвращаются из базы данных как строки Unicode, поля модели, основанные на символах (CharField, TextField, URLField и т.д.), будут содержать значения Unicode, когда Django извлекает данные из базы данных. Это всегда так, даже если данные могут поместиться в строку байтов ASCII.

Вы можете передавать строки байтов при создании модели или заполнении поля, и Django преобразует их в Unicode, когда это необходимо.

Выбор между __str__() и __unicode__()

Примечание

Если вы работаете с Python 3, вы можете пропустить этот раздел, потому что всегда будете создавать __str__() вместо __unicode__(). Если вам нужна совместимость с Python 2, вы можете украсить свой класс модели python_2_unicode_compatible().

Одним из последствий использования Unicode по умолчанию является то, что вам нужно быть внимательным при выводе данных из модели.

В частности, вместо того, чтобы давать вашей модели метод __str__(), мы рекомендуем вам реализовать метод __unicode__(). В методе __unicode__() вы можете без проблем вернуть значения всех своих полей, не беспокоясь о том, помещаются ли они в строку байтов или нет. (Поскольку работает Python, результат __str__() всегда является строкой байтов, даже если вы случайно попытаетесь вернуть объект Unicode).

Вы все равно можете создать метод __str__() в своих моделях, если хотите, но вам не нужно этого делать, если у вас нет веской причины. Базовый класс Django Model автоматически предоставляет реализацию __str__(), которая вызывает __unicode__() и кодирует результат в UTF-8. Это означает, что вам обычно нужно будет реализовать только метод __unicode__() и позволить Django обрабатывать приведение к строке байтов при необходимости.

Обработка в get_absolute_url()

URL могут содержать только символы ASCII. Если вы строите URL из фрагментов данных, которые могут быть не-ASCII, будьте внимательны, чтобы закодировать результаты способом, подходящим для URL. Функция reverse() автоматически обрабатывает это за вас.

Если вы строите URL вручную (т.е., не используя функцию reverse()), вам нужно будет самим позаботиться о кодировании. В этом случае используйте функции iri_to_uri() и urlquote(), которые были описаны выше. Например:

from django.utils.encoding import iri_to_uri
from django.utils.http import urlquote

def get_absolute_url(self):
    url = '/person/%s/?x=0&y=0' % urlquote(self.location)
    return iri_to_uri(url)

Эта функция возвращает правильно закодированный URL, даже если self.location — что-то вроде «Джек посетил Париж & Орлеан». (На самом деле, вызов iri_to_uri() строго не нужен в приведенном выше примере, так как все не-ASCII символы будут удалены при цитировании в первой строке.)

API базы данных

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

from __future__ import unicode_literals

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

Шаблоны

Вы можете использовать либо Unicode, либо строки байтов при создании шаблонов вручную:

from __future__ import unicode_literals
from django.template import Template
t1 = Template(b'This is a bytestring template.')
t2 = Template('This is a Unicode template.')

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

Настройка DEFAULT_CHARSET контролирует кодировку рендеренных шаблонов. По умолчанию она установлена в UTF-8.

Тэги и фильтры шаблонов

Несколько советов, которые следует помнить при написании собственных тэгов и фильтров шаблонов:

  • Всегда возвращайте строки Unicode из метода render() тэга шаблона и из фильтров шаблонов.
  • Используйте force_text() вместо smart_text() в этих местах. Отрисовка тэгов и вызовы фильтров происходят при рендеринге шаблона, поэтому нет никакой выгоды в отсрочке преобразования объектов ленивого перевода в строки. В этом случае проще работать только со строками Unicode.

Файлы

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

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

import sys
sys.getfilesystemencoding()

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

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

В вашей среде разработки вам, возможно, нужно будет добавить настройку в ваш ~.bashrc аналогично::

export LANG="en_US.UTF-8"

Электронная почта

Фреймворк Django для работы с электронной почтой (в django.core.mail) прозрачно поддерживает Unicode. Вы можете использовать данные Unicode в телах сообщений и заголовках. Однако вы все равно обязаны соблюдать требования спецификаций электронной почты, поэтому, например, адреса электронной почты должны использовать только символы ASCII.

Следующий пример кода демонстрирует, что все, кроме адресов электронной почты, может быть не-ASCII:

from __future__ import unicode_literals
from django.core.mail import EmailMessage

subject = 'My visit to Sør-Trøndelag'
sender = 'Arnbjörg Ráðormsdóttir <arnbjorg@example.com>'
recipients = ['Fred <fred@example.com']
body = '...'
msg = EmailMessage(subject, body, sender, recipients)
msg.attach("Une pièce jointe.pdf", "%PDF-1.4.%...", mimetype="application/pdf")
msg.send()

Отправка формы

Отправка 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/1.8/ref/unicode/

Spec-Zone.ru

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