Django 1.5 – это первая версия Django, поддерживающая Python 3. Ваш код может работать с обеими версиями Python: Python 2 (≥ 2.6.5) и Python 3 (≥ 3.2), благодаря приложению six.
Этот документ предназначен в первую очередь для авторов повторно используемых (подключаемых) приложений, которые хотят обеспечить поддержку как Python 2, так и Python 3. Он также описывает принципы написания совместимого кода на Django.
Предполагается, что вы знакомы с отличиями версий Python 2 и 3. Если это не так, сначала прочтите руководство по портированию. Также будет нелишним освежить ваши знания о строках unicode; хорошим способом сделать это является просмотр презентации the Pragmatic Unicode presentation.
Django придерживается стратегии использования совместимого кода. Конечно, вы можете избрать другой подход при написании вашего проекта, особенно в случае, если вам не нужна поддержка Python 2. Но авторам подключаемых приложений всё же рекомендуется придерживаться той же стратегии, что и Django.
Написание совместимого кода будет намного легче, если вы используете Python ≥ 2.6. Django 1.5 включает некоторые инструменты для совместимости приложений, такие как six module
. Для удобства, некоторые псевдонимы были включены уже в Django 1.4.2. Если ваше приложение использует эти инструменты, вам потребуется версия Django ≥ 1.4.2.
Очевидно, написание совместимого кода потребует дополнительных сил, что может привести к разочарованию. Разработчики Django обнаружили, что написание кода на Python 3, который был бы совместим с Python 2, всё же приносит больше пользы, чем наоборот. Это не только сделает ваш код более перспективным, но и сделает доступными преимущества Python 3 (такие как более разумная обработка строк). Работа с Python 2 требует обратной совместимости, и мы как разработчики уже привыкли иметь дело с подобными ограничениями.
Инструменты портирования, предоставляемые Django, соответствуют представленной философии и будут описаны в данной инструкции.
Этот шаг заключается в следующем:
Добавление from __future__ import unicode_literals
первой строкой в ваш модуль Python – будет лучше, если эта строка присутствует в каждом модуле, в противном случае вы должны проследить какой режим используется;
Удаление префикса u
перед строками unicode;
Добавление префикса b
перед байтовыми строками (bytestrings).
Систематическое выполнение этого шага гарантирует обратную совместимость.
Однако, в приложениях Django обычно не используются байтовые строки, поскольку Django предоставляет лишь интерфейс unicode для программистов. При использовании Python 3 не рекомендуется использовать байтовые строки, за исключением двоичных данных или байт-ориентированных интерфейсов. Python 2 делает байтовые строки и строки Unicode взаимозаменяемыми, если они содержат только ASCII данные. Воспользуйтесь этим, чтобы использовать строки Unicode там, где это возможно, и избежать добавления префиксов b
.
Примечание
Префикс u
из Python 2 в версии Python 3.2 вызовет синтаксическую ошибку, но это будет исправлено в Python 3.3 благодаря PEP 414. Таким образом, это преобразование опционально в Python ≥ 3.3. Это всё ещё рекомендуется в философии Python 3 “write Python 3 code”.
Тип строки unicode из Python 2 был переименован в тип str
для Python 3, str()` был переименован в bytes
, исчез тип basestring. Библиотека six предоставляет инструменты, учитывающие эти изменения.
Django также содержит несколько связанных классов и функций в модулях django.utils.encoding
и django.utils.safestring
. Их имена используют тип str
, который неодинаков в Python 2 и Python 3, а также unicode
, которого попросту нет в Python 3. Для того, чтобы избежать неоднозначности и путаницы они были переименованы в bytes
и text
.
Это наименования, которые были изменены в django.utils.encoding
:
Прежнее имя |
Новое имя |
---|---|
smart_str |
smart_bytes |
smart_unicode |
smart_text |
force_unicode |
force_text |
Для поддержания обратной совместимости прежние имена ещё работают на Python 2. В Python 3 smart_str
является псевдонимом smart_text
.
Для обеспечения дальнейшей совместимости новые наименования работают как в Django 1.4.2.
Примечание
django.utils.encoding
был очень сильно изменён в Django 1.5 для улучшения API. Просмотрите соответствующую документацию для получения более подробной информации.
django.utils.safestring
в основном используется через mark_safe()
и mark_for_escaping()
функции, которые не изменились. В случае, если вы используете что-то иное, см. таблицу соответствий:
Прежнее имя |
Новое имя |
---|---|
EscapeString |
EscapeBytes |
EscapeUnicode |
EscapeText |
SafeString |
SafeBytes |
SafeUnicode |
SafeText |
Для поддержания обратной совместимости прежние имена ещё работают на Python 2. В Python 3, EscapeString
и SafeString
являются псевдонимами для EscapeText
и SafeText
соответственно.
Для обеспечения дальнейшей совместимости новые наименования работают как в Django 1.4.2.
__str__()
и __unicode__() методы¶В Python 2 модель объекта имеет __str__()
и __unicode__() методы. Если эти методы существуют, они должны вернуть str
(байт) и unicode
(строку) соответственно.
Оператор print
и str
вызовет встроенный метод __str__()
для удобочитаемого представления объекта. unicode
вызовет метод __unicode__(), если он существует, в противном случае вернёт метод __str__()
и декодирует результат в соответствии с системными настройками. Базовый класс Model
автоматически получит метод __str__()
из __unicode__() путем кодирования в UTF-8.
В python 3 есть объект __str__()
, который должен вернуть str
(строку текста).
(Также можно определить __bytes__()
, но приложения Django практически не используют этот метод, потому что обычно не имеют дело с bytes
.)
Django предоставляет простой способ определить __str__()
и __unicode__() методы, которые работают на Python 2 и 3: необходимо определить метод __str__()
, возвращающий текст и применить декоратор python_2_unicode_compatible()
.
В Python 3, декоратор ничего не выполняет. В Python 2, он определяет соответствующие методы __unicode__() и __str__()
( в процессе замены оригинального __str__()
). Вот пример:
from __future__ import unicode_literals
from django.utils.encoding import python_2_unicode_compatible
@python_2_unicode_compatible
class MyClass(object):
def __str__(self):
return "Instance of my class"
Этот метод является наиболее подходящим для портирования в стиле Django.
Для обеспечения дальнейшей совместимости этот декоратор доступен с версии Django 1.4.2.
Наконец, отметим, что метод __repr__()
должен возвращать str
во всех версиях Python.
dict
и dict
-like классы¶dict.keys()
, dict.items()
и dict.values()
возвращают списки в Python 2 и итераторы в Python 3. QueryDict
и dict
-like классы, определенные в django.utils.datastructures
ведут себя аналогично в Python 3.
six предоставляет инструменты, учитывающие эти изменения: iterkeys()
, iteritems()
и itervalues()
. Вместе с Django поставляется недокументированная функция iterlists()
для django.utils.datastructures.MultiValueDict
и его подклассов.
HttpRequest
и HttpResponse
объекты¶В соответствии с PEP 3333:
заголовки всегда являются строковыми объектами ( str
objects),
поток ввода и вывода всегда является объектом байтов (bytes
objects).
В частности, HttpResponse.content
содержит bytes
, что может стать источником проблем, если вы сравните его со str
в ваших тестах. Предпочтительное решение в таком случае: полагаться на assertContains()
и assertNotContains()
. Эти методы принимают ответ со строками unicode в качестве аргументов.
Следующие рекомендации применяются в исходных кодах Django. Также они рекомендуются для сторонних приложений, которые следуют аналогичной стратегии портирования.
В Python 3 все строки по-умолчанию считаются строками юникода. Тип unicode
из Python 2 аналогичен str
в Python 3, а тип str
становится типом bytes
.
Вы не должны использовать префикс u
перед строками юникода, поскольку подобный синтаксис вызовет ошибку в Python 3.2. И вы обязаны ставить префикс b
перед объектом байта.
Чтобы в Python 2 появилось такое же поведение, всегда импортируйте в модулях unicode_literals
из __future__
:
from __future__ import unicode_literals
my_string = "This is an unicode literal"
my_bytestring = b"This is a bytestring"
Если вам нужны строки байтов в Python 2 и строки юникода в Python 3, используйте встроенную функцию str
:
str('my string')
В Python 3, нет никаких автоматических преобразований между str
и bytes
, а модуль codecs
стал более строгим. Метод str.decode()
всегда возвращает тип bytes
, в свою очередь bytes.decode
всегда возвращает тип str
. В следствие этого иногда может появиться такая необходимость:
value = value.encode('ascii', 'ignore').decode('ascii')
Будьте осторожны с index bytestrings.
При вызове исключения используйте в качестве ключевого слово as
:
try:
...
except MyException as exc:
...
Прежний синтаксис был удалён из Python 3:
try:
...
except MyException, exc: # Don't do that!
...
Синтаксис повторного вызова исключений также подвергся изменениям. См. six.reraise()
.
Используйте шаблоны, данные ниже, для обработки магических методов, переименованных в Python 3.
class MyIterator(six.Iterator):
def __iter__(self):
return self # implement some logic here
def __next__(self):
raise StopIteration # implement some logic here
class MyBoolean(object):
def __bool__(self):
return True # implement some logic here
def __nonzero__(self): # Python 2 compatibility
return type(self).__bool__(self)
class MyDivisible(object):
def __truediv__(self, other):
return self / other # implement some logic here
def __div__(self, other): # Python 2 compatibility
return type(self).__truediv__(self, other)
def __itruediv__(self, other):
return self // other # implement some logic here
def __idiv__(self, other): # Python 2 compatibility
return type(self).__itruediv__(self, other)
Специальные методы ищутся в классе, а не в объекте.
six является канонической библиотекой для одновременной поддержки Python 2 и 3. Ознакомьтесь с его документацией!
Измененная версия six
входит в поставку Django начиная с версии 1.4.2. Вы можете импортировать его как django.utils.six
.
В нём учитываются наиболее распространенные изменения, что позволит написать совместимый код.
Типы basestring
и unicode
были удалены в Python 3, а метод str
изменён. Для проверки этих типов, используйте следующие идиомы
isinstance(myvalue, six.string_types) # replacement for basestring
isinstance(myvalue, six.text_type) # replacement for unicode
isinstance(myvalue, bytes) # replacement for str
Python ≥ 2.6 предоставляет тип bytes
как псевдоним типа str
, так что вам не нужен six.binary_type
.
long
¶Типа long
больше не существует в Python 3. 1L
вызовет синтаксическую ошибку. Используйте six.integer_types
, чтобы проверить является ли число обычным целым или это число типа long
:
isinstance(myvalue, six.integer_types) # replacement for (int, long)
xrange
¶Если вы используете xrange
в Python 2, то импортируйте и используйте six.moves.range
. Также вы можете импортировать six.moves.xrange
(это эквивалент six.moves.range
), но первый вариант позволяет вам просто удалить только строку импорта при отказе от поддержки Python 2.
Некоторые модули были переименованы в Python 3. Модуль ``django.utils.six.moves``(на основе six.moves module
) обеспечивает совместимость с ними и вы можете импортировать их.
Для получения разного кода при использовании Python 2 и Python 3, проверьте six.PY2
:
if six.PY2:
# compatibility code for Python 2
Это решение используется в крайнем случае, только когда six
не может обеспечить необходимый функционал.
Версия six, идущая в поставке с Django(django.utils.six
), имеет несколько дополнений только для внутреннего использования.
Jun 05, 2017