Данные 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принимает любые значения, которые принимаются функциейunicode()Python для обработки ошибок.Если вы передаете
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(). Это немного отличается от семантики встроенной функции Pythonstr(), но это необходимо в нескольких местах в внутренних компонентах 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()являются версиями стандартной функции Pythonurllib.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.9/ref/unicode/