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.
В 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.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 и его подклассов.
В соответствии с 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 больше не существует в Python 3. 1L вызовет синтаксическую ошибку. Используйте six.integer_types, чтобы проверить является ли число обычным целым или это число типа long:
isinstance(myvalue, six.integer_types) # replacement for (int, long)
Если вы используете xrange в Python 2, то импортируйте и используйте six.moves.range. Также вы можете импортировать six.moves.xrange (это эквивалент six.moves.range), но первый вариант позволяет вам просто удалить только строку импорта при отказе от поддержки Python 2.
Некоторые модули были переименованы в Python 3. Модуль ``django.utils.six.moves``(на основе six.moves module) обеспечивает совместимость с ними и вы можете импортировать их.
Версия six, идущая в поставке с Django(django.utils.six), имеет несколько дополнений только для внутреннего использования.
Mar 31, 2016