Django 1.0 breaks compatibility with 0.96 in some areas.
This guide will help you port 0.96 projects and apps to 1.0. The first part of this document includes the common changes needed to run with 1.0. If after going through the first part your code still breaks, check the section Less-common Changes for a list of a bunch of less-common compatibility issues.
Zobacz także
The 1.0 release notes. That document explains the new features in 1.0 more deeply; the porting guide is more concerned with helping you quickly update your code.
This section describes the changes between 0.96 and 1.0 that most users will need to make.
Change string literals ('foo'
) into Unicode literals (u'foo'
). Django
now uses Unicode strings throughout. In most places, raw strings will continue
to work, but updating to use Unicode literals will prevent some obscure
problems.
See Unicode data for full details.
Common changes to your models file:
maxlength
to max_length
¶Rename your maxlength
argument to max_length
(this was changed to be
consistent with form fields):
__str__
with __unicode__
¶Replace your model’s __str__
function with a __unicode__
method, and
make sure you use Unicode (u'foo'
) in that method.
prepopulated_from
¶Remove the prepopulated_from
argument on model fields. It’s no longer valid
and has been moved to the ModelAdmin
class in admin.py
. See the
admin, below, for more details about changes to the admin.
core
¶Remove the core
argument from your model fields. It is no longer
necessary, since the equivalent functionality (part of inline editing) is handled differently by the admin interface now. You don’t
have to worry about inline editing until you get to the admin section,
below. For now, remove all references to core
.
class Admin:
with admin.py
¶Remove all your inner class Admin
declarations from your models. They won’t
break anything if you leave them, but they also won’t do anything. To register
apps with the admin you’ll move those declarations to an admin.py
file;
see the admin below for more details.
Zobacz także
A contributor to djangosnippets has written a script that’ll scan your models.py and generate a corresponding admin.py.
Below is an example models.py
file with all the changes you’ll need to make:
Old (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)
New (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)
New (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)
One of the biggest changes in 1.0 is the new admin. The Django administrative
interface (django.contrib.admin
) has been completely refactored; admin
definitions are now completely decoupled from model definitions, the framework
has been rewritten to use Django’s new form-handling library and redesigned with
extensibility and customization in mind.
Practically, this means you’ll need to rewrite all of your class Admin
declarations. You’ve already seen in models above how to replace your class
Admin
with a admin.site.register()
call in an admin.py
file. Below are
some more details on how to rewrite that Admin
declaration into the new
syntax.
The new edit_inline
options have all been moved to admin.py
. Here’s an
example:
Old (0.96):
class Parent(models.Model):
...
class Child(models.Model):
parent = models.ForeignKey(Parent, edit_inline=models.STACKED, num_in_admin=3)
New (1.0):
class ChildInline(admin.StackedInline):
model = Child
extra = 3
class ParentAdmin(admin.ModelAdmin):
model = Parent
inlines = [ChildInline]
admin.site.register(Parent, ParentAdmin)
See InlineModelAdmin objects for more details.
fields
, or use fieldsets
¶The old fields
syntax was quite confusing, and has been simplified. The old
syntax still works, but you’ll need to use fieldsets
instead.
Old (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'}),
)
New (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'}),
)
Zobacz także
urls.py
¶If you’re using the admin site, you need to update your root urls.py
.
Old (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 ...
)
New (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
instead of newforms
¶Replace django.newforms
with django.forms
– Django 1.0 renamed the
newforms
module (introduced in 0.96) to plain old forms
. The
oldforms
module was also removed.
If you’re already using the newforms
library, and you used our recommended
import
statement syntax, all you have to do is change your import
statements.
Old:
from django import newforms as forms
New:
from django import forms
If you’re using the old forms system (formerly known as django.forms
and
django.oldforms
), you’ll have to rewrite your forms. A good place to start
is the forms documentation
Replace use of uploaded files – that is, entries in request.FILES
– as
simple dictionaries with the new
UploadedFile
. The old dictionary
syntax no longer works.
Thus, in a view like:
def my_view(request):
f = request.FILES['file_field_name']
...
…you’d need to make the following changes:
Old (0.96) | New (1.0) |
---|---|
f['content'] |
f.read() |
f['filename'] |
f.name |
f['content-type'] |
f.content_type |
The internal implementation of django.db.models.FileField
have changed.
A visible result of this is that the way you access special attributes (URL,
filename, image size, etc.) of these model fields has changed. You will need to
make the following changes, assuming your model’s
FileField
is called myfile
:
Old (0.96) | New (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 |
Note that the width
and height
attributes only make sense for
ImageField
fields. More details can be found in the
model API documentation.
Paginator
instead of ObjectPaginator
¶The ObjectPaginator
in 0.96 has been removed and replaced with an improved
version, django.core.paginator.Paginator
.
By default, the template system now automatically HTML-escapes the output of every variable. To learn more, see Automatic HTML escaping.
To disable auto-escaping for an individual variable, use the safe
filter:
This will be escaped: {{ data }}
This will not be escaped: {{ data|safe }}
To disable auto-escaping for an entire template, wrap the template (or just a
particular section of the template) in the autoescape
tag:
{% autoescape off %}
... unescaped template content here ...
{% endautoescape %}
The following changes are smaller, more localized changes. They should only affect more advanced users, but it’s probably worth reading through the list and checking your code for these things.
**kwargs
to any registered signal handlers.Signal
object instead of through module methods in
django.dispatch.dispatcher
.Anonymous
and Any
sender options; they no longer
exist. You can still receive signals sent by any sender by using
sender=None
django.dispatch.Signal
instead of anonymous objects.Here’s quick summary of the code changes you’ll need to make:
Old (0.96) | New (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) |
If you were using Django 0.96’s django.contrib.comments
app, you’ll need to
upgrade to the new comments app introduced in 1.0. See the upgrade guide
for details.
django.contrib.localflavor.usa
has been renamed to
django.contrib.localflavor.us
. This change was made to match the naming
scheme of other local flavors. To migrate your code, all you need to do is
change the imports.
SessionBase.get_new_session_key()
has been renamed to
_get_new_session_key()
. get_new_session_object()
no longer exists.
save()
¶Previously, loading a row automatically ran the model’s save()
method. This
is no longer the case, so any fields (for example: timestamps) that were
auto-populated by a save()
now need explicit values in any fixture.
The old EnvironmentError
has split into an
ImportError
when Django fails to find the settings module
and a RuntimeError
when you try to reconfigure settings
after having already used them.
LOGIN_URL
został przeniesiony¶Stała LOGIN_URL
została przeniesiona z django.contrib.auth
do modułu settings
. Zamiast używać from django.contrib.auth import LOGIN_URL
, odwołaj się do settings.LOGIN_URL
.
APPEND_SLASH
zostało zaktualizowane¶In 0.96, if a URL didn’t end in a slash or have a period in the final
component of its path, and APPEND_SLASH
was True, Django would
redirect to the same URL, but with a slash appended to the end. Now, Django
checks to see whether the pattern without the trailing slash would be matched
by something in your URL patterns. If so, no redirection takes place, because
it is assumed you deliberately wanted to catch that pattern.
Dla większości osób, ta zmiana nie będzie wymagała wprowadzenia żadnych zmian. Jeśli jednak posiadasz patterny URL wyglądające tak jak ten:
r'/some_prefix/(.*)$'
Poprzednio te patterny zostałyby przekierowane tak, aby zawierały końcowy ukośnik. Jeśli chcesz zawsze posiadać ukośnik na takich URL-ach, przepisz pattern na:
r'/some_prefix/(.*/)$'
get()
¶Manager-y od teraz zwracają wyjątek MultipleObjectsReturned
zamiast AssertionError
:
Old (0.96):
try:
Model.objects.get(...)
except AssertionError:
handle_the_error()
New (1.0):
try:
Model.objects.get(...)
except Model.MultipleObjectsReturned:
handle_the_error()
LazyDate
został zwolniony¶Klasa pomocnicza LazyDate
już nie istnieje.
Domyślne wartości pól i argumentów query mogą być także obiektami wywoływalnymi, dlatego instancje LazyDate
mogą być zamienione na referencję do datetime.datetime.now
:
Old (0.96):
class Article(models.Model):
title = models.CharField(maxlength=100)
published = models.DateField(default=LazyDate())
New (1.0):
import datetime
class Article(models.Model):
title = models.CharField(max_length=100)
published = models.DateField(default=datetime.datetime.now)
DecimalField
, a FloatField
jest od teraz poprawnym float-em.¶Old (0.96):
class MyModel(models.Model):
field_name = models.FloatField(max_digits=10, decimal_places=3)
...
New (1.0):
class MyModel(models.Model):
field_name = models.DecimalField(max_digits=10, decimal_places=3)
...
Jeśli zapomnisz dokonać tej zmiany, zobaczysz błędu mówiące, że FloatField
nie przyjmuje atrybutu max_digits
w __init__
, gdyż nowy FloatField
nie przyjmuje argumentów dotyczących jego precyzji.
Jeśli używasz MySQL albo PostgreSQL, żadne dodatkowe zmiany nie są wymagane. Typy kolumn bazy danych dla DecimalField
są takie same jak dla starego FloatField
.
Jeśli używasz SQLite, musisz wymusić na bazie danych traktowanie niektórych kolumn jako typ decimal zamiast jako float. Aby to zrobić, będziesz musiał ponownie wczytać swoje dane. Zrób to dopiero po tym jak zaktualizujesz kod Django i zmienisz typ pól na DecimalField
.
Ostrzeżenie
Przed podjęciem jakichkolwiek działań, wykonaj kopię swojej bazy danych!
Dla SQLite oznacza to stworzenie kopii pojedynczego pliku, którzy przechowuje Twoją bazę danych (nazwa tego pliku jest zdefiniowana w stałej DATABASE_NAME
w pliku settings.py).
To upgrade each application to use a DecimalField
, you can do the
following, replacing <app>
in the code below with each app’s name:
$ ./manage.py dumpdata --format=xml <app> > data-dump.xml
$ ./manage.py reset <app>
$ ./manage.py loaddata data-dump.xml
Notatki:
DecimalField
is not used in any of the apps shipped with Django prior
to this change being made, so you do not need to worry about performing
this procedure for any of the standard Django models.If something goes wrong in the above process, just copy your backed up database file over the original file and start again.
django.views.i18n.set_language()
now requires a POST request¶Previously, a GET request was used. The old behavior meant that state (the locale used to display the site) could be changed by a GET request, which is against the HTTP specification’s recommendations. Code calling this view must ensure that a POST request is now made, instead of a GET. This means you can no longer use a link to access the view, but must use a form submission of some kind (e.g. a button).
_()
is no longer in builtins¶_()
(the callable object whose name is a single underscore) is no longer
monkeypatched into builtins – that is, it’s no longer available magically in
every module.
If you were previously relying on _()
always being present, you should now
explicitly import ugettext
or ugettext_lazy
, if appropriate, and alias
it to _
yourself:
from django.utils.translation import ugettext as _
HttpRequest
¶HttpRequest
objects no longer directly support dictionary-style
access; previously, both GET
and POST
data were directly
available on the HttpRequest
object (e.g., you could check for a
piece of form data by using if 'some_form_key' in request
or by
reading request['some_form_key']
. This is no longer supported; if
you need access to the combined GET
and POST
data, use
request.REQUEST
instead.
It is strongly suggested, however, that you always explicitly look in
the appropriate dictionary for the type of request you expect to
receive (request.GET
or request.POST
); relying on the combined
request.REQUEST
dictionary can mask the origin of incoming data.
HTTPResponse
¶Nazwa django.http.HttpResponse.headers
została zmieniona na _headers
a HttpResponse
obsługuje od teraz bezpośrednie sprawdzanie występowania kluczy. Używaj więc if header in response:
zamiast if header in response.headers:
.
Klasy relacji generycznych – GenericForeignKey
i GenericRelation
– zostały przeniesione do modułu django.contrib.contenttypes
.
django.test.Client.login()
został zmieniony¶Old (0.96):
from django.test import Client
c = Client()
c.login('/path/to/login','myuser','mypassword')
New (1.0):
# ... same as above, but then:
c.login(username='myuser', password='mypassword')
django.core.management
został znacznie zrefaktorowany.
Calls to management services in your code now need to use
call_command
. For example, if you have some test code that calls flush and
load_data:
from django.core import management
management.flush(verbosity=0, interactive=False)
management.load_data(['test_data'], verbosity=0)
…you’ll need to change this code to read:
from django.core import management
management.call_command('flush', verbosity=0, interactive=False)
management.call_command('loaddata', 'test_data', verbosity=0)
django-admin.py
and manage.py
now require subcommands to precede
options. So:
$ django-admin.py --settings=foo.bar runserver
…no longer works and should be changed to:
$ django-admin.py runserver --settings=foo.bar
Feed.__init__
has changed¶The __init__()
method of the syndication framework’s Feed
class now
takes an HttpRequest
object as its second parameter, instead of the feed’s
URL. This allows the syndication framework to work without requiring the sites
framework. This only affects code that subclasses Feed
and overrides the
__init__()
method, and code that calls Feed.__init__()
directly.
SortedDictFromList
is gone¶django.newforms.forms.SortedDictFromList
was removed.
django.utils.datastructures.SortedDict
can now be instantiated with
a sequence of tuples.
To update your code:
django.utils.datastructures.SortedDict
wherever you were
using django.newforms.forms.SortedDictFromList
.django.utils.datastructures.SortedDict.copy
doesn’t
return a deepcopy as SortedDictFromList.copy()
did, you will need
to update your code if you were relying on a deepcopy. Do this by using
copy.deepcopy
directly.Almost all of the database backend-level functions have been renamed and/or
relocated. None of these were documented, but you’ll need to change your code
if you’re using any of these functions, all of which are in django.db
:
Old (0.96) | New (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 |
gru 02, 2019