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