Перенос ваших приложений с Django 0.96 на 1.0¶
Django 1.0 нарушает совместимость с 0.96 в некоторых областях.
Это руководство поможет вам перенести проекты и приложения версии 0.96 на версию 1.0. Первая часть этого документа включает общие изменения, необходимые для работы с 1.0. Если после изучения первой части ваш код все еще ломается, обратитесь к разделу Less-common Changes, где приведен список менее распространенных проблем совместимости.
См.также
1.0 release notes. Этот документ более глубоко объясняет новые возможности версии 1.0; руководство по переносу больше направлено на то, чтобы помочь вам быстро обновить ваш код.
Общие изменения¶
В этом разделе описаны изменения между 0.96 и 1.0, которые потребуется внести большинству пользователей.
Использовать Юникод¶
Измените строковые литералы ('foo'
) на литералы Unicode (u'foo'
). Django теперь использует строки Unicode повсеместно. В большинстве мест необработанные строки будут продолжать работать, но обновление на использование литералов Unicode предотвратит некоторые неясные проблемы.
Подробную информацию см. в разделе Данные Юникода.
Модели¶
Общие изменения в файле моделей:
Переименуйте maxlength
в max_length
¶
Переименуйте аргумент maxlength
в max_length
(он был изменен для соответствия полям формы):
Замените __str__
на __unicode__
¶
Замените функцию __str__
вашей модели на метод __unicode__
, и убедитесь, что вы use Unicode (u'foo'
) в этом методе.
Удалить prepopulated_from
¶
Удалите аргумент prepopulated_from
для полей модели. Он больше не действителен и был перемещен в класс ModelAdmin
в admin.py
. Более подробно об изменениях в админке смотрите the admin, ниже.
Удалить core
¶
Удалите аргумент core
из полей вашей модели. В нем больше нет необходимости, поскольку эквивалентная функциональность (часть inline editing) теперь по-другому обрабатывается интерфейсом администратора. Вам не нужно беспокоиться о встроенном редактировании, пока вы не перейдете к разделу the admin, ниже. Пока же удалите все ссылки на core
.
Замените class Admin:
на admin.py
¶
Удалите все внутренние объявления class Admin
из ваших моделей. Они ничего не сломают, если вы их оставите, но они также ничего не сделают. Чтобы зарегистрировать приложения в администраторе, вы перенесете эти объявления в файл admin.py
; подробнее см. ниже the admin.
См.также
Автор djangosnippets написал сценарий, который scan your models.py and generate a corresponding admin.py.
Пример¶
Ниже приведен пример файла models.py
со всеми изменениями, которые вам нужно будет внести:
Старый (0.96) models.py
:
class Author(models.Model):
first_name = models.CharField(maxlength=30)
last_name = models.CharField(maxlength=30)
slug = models.CharField(maxlength=60, prepopulate_from=('first_name', 'last_name'))
class Admin:
list_display = ['first_name', 'last_name']
def __str__(self):
return '%s %s' % (self.first_name, self.last_name)
Новый (1.0) models.py
:
class Author(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
slug = models.CharField(max_length=60)
def __unicode__(self):
return u'%s %s' % (self.first_name, self.last_name)
Новый (1.0) admin.py
:
from django.contrib import admin
from models import Author
class AuthorAdmin(admin.ModelAdmin):
list_display = ['first_name', 'last_name']
prepopulated_fields = {
'slug': ('first_name', 'last_name')
}
admin.site.register(Author, AuthorAdmin)
Администратор¶
Одним из самых больших изменений в версии 1.0 является новый администратор. Административный интерфейс Django (django.contrib.admin
) был полностью переработан; определения администратора теперь полностью отделены от определений модели, фреймворк был переписан для использования новой библиотеки обработки форм Django и переработан с учетом расширяемости и настройки.
Практически это означает, что вам придется переписать все ваши объявления class Admin
. Вы уже видели в models выше, как заменить class Admin
на admin.site.register()
вызов в admin.py
файле. Ниже приведены некоторые подробности о том, как переписать это объявление Admin
в новом синтаксисе.
Используйте новый встроенный синтаксис¶
Все новые опции edit_inline
были перенесены в admin.py
. Вот пример:
Старый (0,96):
class Parent(models.Model):
...
class Child(models.Model):
parent = models.ForeignKey(Parent, edit_inline=models.STACKED, num_in_admin=3)
Новый (1.0):
class ChildInline(admin.StackedInline):
model = Child
extra = 3
class ParentAdmin(admin.ModelAdmin):
model = Parent
inlines = [ChildInline]
admin.site.register(Parent, ParentAdmin)
Более подробную информацию см. в разделе InlineModelAdmin объекты.
Упростите fields
, или используйте fieldsets
¶
Старый синтаксис fields
был довольно запутанным, и теперь он упрощен. Старый синтаксис по-прежнему работает, но вместо него нужно использовать fieldsets
.
Старый (0,96):
class ModelOne(models.Model):
...
class Admin:
fields = (
(None, {'fields': ('foo','bar')}),
)
class ModelTwo(models.Model):
...
class Admin:
fields = (
('group1', {'fields': ('foo','bar'), 'classes': 'collapse'}),
('group2', {'fields': ('spam','eggs'), 'classes': 'collapse wide'}),
)
Новый (1.0):
class ModelOneAdmin(admin.ModelAdmin):
fields = ('foo', 'bar')
class ModelTwoAdmin(admin.ModelAdmin):
fieldsets = (
('group1', {'fields': ('foo','bar'), 'classes': 'collapse'}),
('group2', {'fields': ('spam','eggs'), 'classes': 'collapse wide'}),
)
См.также
- Более подробную информацию об изменениях и их причинах можно найти на сайте NewformsAdminBranch wiki page
- Новый администратор поставляется с тонной новых возможностей; вы можете прочитать о них в admin documentation.
URLs¶
Обновите свой корень urls.py
¶
Если вы используете админку сайта, вам нужно обновить корень urls.py
.
Старый (0.96) urls.py
:
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^admin/', include('django.contrib.admin.urls')),
# ... the rest of your URLs here ...
)
Новый (1.0) urls.py
:
from django.conf.urls.defaults import *
# The next two lines enable the admin and load each admin.py file:
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
(r'^admin/(.*)', admin.site.root),
# ... the rest of your URLs here ...
)
Представления¶
Используйте django.forms
вместо newforms
¶
Замените django.newforms
на django.forms
– Django 1.0 переименовал модуль newforms
(представленный в 0.96) в обычный старый forms
. Модуль oldforms
также был удален.
Если вы уже используете библиотеку newforms
, и вы использовали рекомендуемый нами синтаксис оператора import
, все, что вам нужно сделать, это изменить свои операторы импорта.
Старый:
from django import newforms as forms
Новое:
from django import forms
Если вы используете старую систему форм (ранее известную как django.forms
и django.oldforms
), вам придется переписать свои формы. Хорошее место для начала - forms documentation
Работа с загруженными файлами с помощью нового API¶
Замените использование загруженных файлов - то есть записей в request.FILES
- в качестве простых словарей новым UploadedFile
. Старый синтаксис словарей больше не работает.
Таким образом, в таком представлении, как:
def my_view(request):
f = request.FILES['file_field_name']
...
…вам нужно внести следующие изменения:
Старый (0,96) | Новый (1.0) |
---|---|
f['content'] |
f.read() |
f['filename'] |
f.name |
f['content-type'] |
f.content_type |
Работа с полями файлов с помощью нового API¶
Внутренняя реализация django.db.models.FileField
изменилась. Видимым результатом этого является изменение способа доступа к специальным атрибутам (URL, имя файла, размер изображения и т.д.) этих полей модели. Вам нужно будет внести следующие изменения, если предположить, что FileField
вашей модели называется myfile
:
Старый (0,96) | Новый (1.0) |
---|---|
myfile.get_content_filename() |
myfile.content.path |
myfile.get_content_url() |
myfile.content.url |
myfile.get_content_size() |
myfile.content.size |
myfile.save_content_file() |
myfile.content.save() |
myfile.get_content_width() |
myfile.content.width |
myfile.get_content_height() |
myfile.content.height |
Обратите внимание, что атрибуты width
и height
имеют смысл только для полей ImageField
. Более подробную информацию можно найти в документации по model API.
Используйте Paginator
вместо ObjectPaginator
¶
ObjectPaginator
в версии 0.96 был удален и заменен улучшенной версией django.core.paginator.Paginator
.
Шаблоны¶
Научитесь любить автоэскейп¶
По умолчанию система шаблонов теперь автоматически выводит HTML-эскейп каждой переменной. Чтобы узнать больше, смотрите Автоматическое экранирование HTML.
Чтобы отключить автозавершение для отдельной переменной, используйте фильтр safe
:
This will be escaped: {{ data }}
This will not be escaped: {{ data|safe }}
Чтобы отключить автозавершение для всего шаблона, оберните шаблон (или только определенный раздел шаблона) в тег autoescape
:
{% autoescape off %}
... unescaped template content here ...
{% endautoescape %}
Менее распространенные изменения¶
Следующие изменения являются более мелкими, локализованными изменениями. Они должны повлиять только на более опытных пользователей, но, вероятно, стоит ознакомиться со списком и проверить свой код на наличие этих вещей.
Сигналы¶
- Добавьте
**kwargs
ко всем зарегистрированным обработчикам сигналов. - Подключение, отключение и отправка сигналов через методы на объекте
Signal
вместо методов модуля вdjango.dispatch.dispatcher
. - Удалите любое использование опций отправителя
Anonymous
иAny
; они больше не существуют. Вы все еще можете получать сигналы, посылаемые любым отправителем, используяsender=None
- Сделайте все объявленные вами пользовательские сигналы экземплярами
django.dispatch.Signal
вместо анонимных объектов.
Вот краткое описание изменений в коде, которые вам нужно будет сделать:
Старый (0,96) | Новый (1.0) |
---|---|
def callback(sender) |
def callback(sender, **kwargs) |
sig = object() |
sig = django.dispatch.Signal() |
dispatcher.connect(callback, sig) |
sig.connect(callback) |
dispatcher.send(sig, sender) |
sig.send(sender) |
dispatcher.connect(callback, sig, sender=Any) |
sig.connect(callback, sender=None) |
Коментарі¶
Если вы использовали приложение django.contrib.comments
в Django 0.96, вам необходимо перейти на новое приложение комментариев, представленное в 1.0. Подробности смотрите в руководстве по обновлению.
Теги шаблона¶
Местные ароматы¶
Местный колорит США¶
django.contrib.localflavor.usa
был переименован в django.contrib.localflavor.us
. Это изменение было сделано для того, чтобы соответствовать схеме именования других локальных вкусов. Чтобы перенести ваш код, достаточно изменить импорты.
Сессии¶
Получение нового ключа сеанса¶
SessionBase.get_new_session_key()
был переименован в _get_new_session_key()
. get_new_session_object()
больше не существует.
Приспособления¶
Загрузка строки больше не вызывает save()
¶
Ранее загрузка строки автоматически запускала метод save()
модели. Теперь это не так, поэтому любые поля (например, метки времени), которые автоматически заполнялись методом save()
, теперь нуждаются в явных значениях в любом приспособлении.
Настройки¶
Улучшенные исключения¶
Старый EnvironmentError
разделился на ImportError
, когда Django не может найти модуль настроек, и RuntimeError
, когда вы пытаетесь перенастроить настройки после того, как они уже были использованы.
LOGIN_URL
переехал¶
Константа LOGIN_URL
переместилась из django.contrib.auth
в модуль settings
. Вместо использования from django.contrib.auth import LOGIN_URL
обратитесь к settings.LOGIN_URL
.
APPEND_SLASH
поведение было обновлено¶
В версии 0.96, если URL не заканчивался косой чертой или точкой в последнем компоненте пути, а APPEND_SLASH
был равен True, Django перенаправлял на тот же URL, но с косой чертой в конце. Теперь Django проверяет, соответствует ли шаблон без косой черты чему-то из ваших шаблонов URL. Если да, то перенаправления не происходит, так как предполагается, что вы намеренно хотели поймать этот шаблон.
Для большинства людей это не потребует никаких изменений. Однако у некоторых людей шаблоны URL выглядят следующим образом:
r'/some_prefix/(.*)$'
Ранее эти шаблоны перенаправлялись с использованием косой черты. Если вы хотите, чтобы в таких URL всегда присутствовала косая черта, перепишите шаблон как:
r'/some_prefix/(.*/)$'
Небольшие изменения в моделях¶
Исключение, отличное от get()
¶
Менеджеры теперь возвращают исключение MultipleObjectsReturned
вместо AssertionError
:
Старый (0,96):
try:
Model.objects.get(...)
except AssertionError:
handle_the_error()
Новый (1.0):
try:
Model.objects.get(...)
except Model.MultipleObjectsReturned:
handle_the_error()
LazyDate
был уволен¶
Класс-помощник LazyDate
больше не существует.
Значения полей по умолчанию и аргументы запроса могут быть вызываемыми объектами, поэтому экземпляры LazyDate
могут быть заменены ссылкой на datetime.datetime.now
:
Старый (0,96):
class Article(models.Model):
title = models.CharField(maxlength=100)
published = models.DateField(default=LazyDate())
Новый (1.0):
import datetime
class Article(models.Model):
title = models.CharField(max_length=100)
published = models.DateField(default=datetime.datetime.now)
DecimalField
является новым, а FloatField
теперь является правильным float¶
Старый (0,96):
class MyModel(models.Model):
field_name = models.FloatField(max_digits=10, decimal_places=3)
...
Новый (1.0):
class MyModel(models.Model):
field_name = models.DecimalField(max_digits=10, decimal_places=3)
...
Если вы забудете сделать это изменение, вы увидите ошибки о том, что FloatField
не принимает атрибут max_digits
в __init__
, потому что новый FloatField
не принимает аргументы, связанные с точностью.
Если вы используете MySQL или PostgreSQL, никаких дополнительных изменений не требуется. Типы столбцов базы данных для DecimalField
такие же, как и для старого FloatField
.
Если вы используете SQLite, вам необходимо заставить базу данных отображать соответствующие столбцы как десятичные типы, а не как плавающие. Для этого необходимо перезагрузить данные. Сделайте это после того, как вы перейдете на использование DecimalField
в своем коде и обновите код Django.
Предупреждение
Сначала создайте резервную копию базы данных!
Для SQLite это означает создание копии единственного файла, в котором хранится база данных (имя этого файла - DATABASE_NAME
в вашем файле settings.py).
Чтобы перевести каждое приложение на использование DecimalField
, вы можете сделать следующее, заменив <app>
в приведенном ниже коде на имя каждого приложения:
$ ./manage.py dumpdata --format=xml <app> > data-dump.xml
$ ./manage.py reset <app>
$ ./manage.py loaddata data-dump.xml
Примечания:
- Важно, чтобы вы не забыли использовать формат XML на первом этапе этого процесса. Мы используем особенность дампов данных XML, которая делает возможным перенос плавающих чисел в десятичные с помощью SQLite.
- На втором этапе вам будет предложено подтвердить, что вы готовы потерять данные для данного приложения (приложений). Скажите «да»; мы восстановим эти данные на третьем шаге.
DecimalField
не используется ни в одном из приложений, поставляемых с Django до внесения этого изменения, поэтому вам не нужно беспокоиться о выполнении этой процедуры для любой из стандартных моделей Django.
Если что-то пошло не так в описанном выше процессе, просто скопируйте файл резервной копии базы данных поверх оригинального файла и начните сначала.
Интернационализация¶
django.views.i18n.set_language()
теперь требует POST-запрос¶
Ранее использовался запрос GET. Старое поведение означало, что state (локаль, используемая для отображения сайта) может быть изменена запросом GET, что противоречит рекомендациям спецификации HTTP. Код, вызывающий это представление, должен убедиться, что теперь выполняется POST-запрос, а не GET. Это означает, что вы больше не можете использовать ссылку для доступа к представлению, а должны использовать форму отправки какого-либо запроса (например, кнопку).
_()
больше нет во встроенных модулях¶
_()
(вызываемый объект, имя которого состоит из одного символа подчеркивания) больше не встраивается во встроенные модули - то есть, он больше не доступен волшебным образом в каждом модуле.
Если вы ранее полагались на то, что _()
всегда присутствует, то теперь вам следует явно импортировать ugettext
или ugettext_lazy
, если это уместно, и псевдоним _
самостоятельно:
from django.utils.translation import ugettext as _
Объекты запросов/ответов HTTP¶
Доступ к словарю HttpRequest
¶
Объекты HttpRequest
больше не поддерживают прямой доступ в стиле словаря; ранее данные GET
и POST
были доступны непосредственно на объекте HttpRequest
(например, вы могли проверить наличие части данных формы с помощью if 'some_form_key' in request
или путем чтения request['some_form_key']
. Это больше не поддерживается; если вам нужен доступ к комбинированным данным GET
и POST
, используйте вместо этого request.REQUEST
.
Однако настоятельно рекомендуется всегда явно искать в соответствующем словаре тип запроса, который вы ожидаете получить (request.GET
или request.POST
); полагаясь на комбинированный словарь request.REQUEST
, можно скрыть происхождение входящих данных.
Доступ к заголовкам HTTPResponse
¶
django.http.HttpResponse.headers
был переименован в _headers
, а HttpResponse
теперь поддерживает проверку содержимого напрямую. Поэтому используйте if header in response:
вместо if header in response.headers:
.
Общие отношения¶
Общие отношения были перенесены из ядра¶
Классы общих отношений – GenericForeignKey
и GenericRelation
– переместились в модуль django.contrib.contenttypes
.
Тестирование¶
django.test.Client.login()
изменилось¶
Старый (0,96):
from django.test import Client
c = Client()
c.login('/path/to/login','myuser','mypassword')
Новый (1.0):
# ... same as above, but then:
c.login(username='myuser', password='mypassword')
Команды управления¶
Выполнение команд управления из вашего кода¶
django.core.management
был значительно переработан.
Вызовы служб управления в вашем коде теперь должны использовать call_command
. Например, если у вас есть тестовый код, который вызывает flush и load_data:
from django.core import management
management.flush(verbosity=0, interactive=False)
management.load_data(['test_data'], verbosity=0)
…вам нужно изменить этот код следующим образом:
from django.core import management
management.call_command('flush', verbosity=0, interactive=False)
management.call_command('loaddata', 'test_data', verbosity=0)
Теперь подкоманды должны предшествовать опциям¶
django-admin.py
и manage.py
теперь требуют, чтобы подкоманды предшествовали опциям. Таким образом:
$ django-admin.py --settings=foo.bar runserver
…больше не работает и должен быть заменен на:
$ django-admin.py runserver --settings=foo.bar
Синдикация¶
Feed.__init__
изменилось¶
Метод __init__()
класса Feed
фреймворка синдикации теперь принимает объект HttpRequest
в качестве второго параметра, вместо URL фида. Это позволяет фреймворку синдикации работать без использования фреймворка сайтов. Это влияет только на код, который является подклассом Feed
и переопределяет метод __init__()
, а также на код, который вызывает Feed.__init__()
напрямую.
Структуры данных¶
SortedDictFromList
исчезла¶
django.newforms.forms.SortedDictFromList
был удален. django.utils.datastructures.SortedDict
теперь может быть инстанцирован последовательностью кортежей.
Чтобы обновить свой код:
- Используйте
django.utils.datastructures.SortedDict
везде, где вы использовалиdjango.newforms.forms.SortedDictFromList
. - Поскольку
django.utils.datastructures.SortedDict.copy
не возвращает глубокое копирование, как это делаетSortedDictFromList.copy()
, вам нужно будет обновить свой код, если вы полагались на глубокое копирование. Сделайте это, используяcopy.deepcopy
напрямую.
Функции бэкенда базы данных¶
Функции бэкенда базы данных были переименованы¶
Почти все функции уровня бэкенда базы данных были переименованы и/или перемещены. Ни одна из них не была документирована, но вам придется изменить свой код, если вы используете какую-либо из этих функций, все они находятся в django.db
:
Старый (0,96) | Новый (1.0) |
---|---|
backend.get_autoinc_sql |
connection.ops.autoinc_sql |
backend.get_date_extract_sql |
connection.ops.date_extract_sql |
backend.get_date_trunc_sql |
connection.ops.date_trunc_sql |
backend.get_datetime_cast_sql |
connection.ops.datetime_cast_sql |
backend.get_deferrable_sql |
connection.ops.deferrable_sql |
backend.get_drop_foreignkey_sql |
connection.ops.drop_foreignkey_sql |
backend.get_fulltext_search_sql |
connection.ops.fulltext_search_sql |
backend.get_last_insert_id |
connection.ops.last_insert_id |
backend.get_limit_offset_sql |
connection.ops.limit_offset_sql |
backend.get_max_name_length |
connection.ops.max_name_length |
backend.get_pk_default_value |
connection.ops.pk_default_value |
backend.get_random_function_sql |
connection.ops.random_function_sql |
backend.get_sql_flush |
connection.ops.sql_flush |
backend.get_sql_sequence_reset |
connection.ops.sequence_reset_sql |
backend.get_start_transaction_sql |
connection.ops.start_transaction_sql |
backend.get_tablespace_sql |
connection.ops.tablespace_sql |
backend.quote_name |
connection.ops.quote_name |
backend.get_query_set_class |
connection.ops.query_set_class |
backend.get_field_cast_sql |
connection.ops.field_cast_sql |
backend.get_drop_sequence |
connection.ops.drop_sequence_sql |
backend.OPERATOR_MAPPING |
connection.operators |
backend.allows_group_by_ordinal |
connection.features.allows_group_by_ordinal |
backend.allows_unique_and_pk |
connection.features.allows_unique_and_pk |
backend.autoindexes_primary_keys |
connection.features.autoindexes_primary_keys |
backend.needs_datetime_string_cast |
connection.features.needs_datetime_string_cast |
backend.needs_upper_for_iops |
connection.features.needs_upper_for_iops |
backend.supports_constraints |
connection.features.supports_constraints |
backend.supports_tablespaces |
connection.features.supports_tablespaces |
backend.uses_case_insensitive_names |
connection.features.uses_case_insensitive_names |
backend.uses_custom_queryset |
connection.features.uses_custom_queryset |