Данные 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принимает любые значения, которые принимаются функцией Pythonunicode()для обработки ошибок.Если вы передаете
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.
Обычно вам нужно использовать только force_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. Если ваша среда не настроена правильно, вы столкнётесь с исключениями UnicodeEncodeError при сохранении файлов с именами файлов, содержащими символы, не входящие в ASCII.
Поддержка файловой системы для имён файлов 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.11/ref/unicode/