Данные 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.
Обычно вам нужно будет использовать только 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.10/ref/unicode/