Porting your apps from Django 0.96 to 1.0Link to this heading

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.

Common changesLink to this heading

This section describes the changes between 0.96 and 1.0 that most users will need to make.

Use UnicodeLink to this heading

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.

ModeleLink to this heading

Common changes to your models file:

Rename maxlength to max_lengthLink to this heading

Rename your maxlength argument to max_length (this was changed to be consistent with form fields):

Replace __str__ with __unicode__Link to this heading

Replace your model’s __str__ function with a __unicode__ method, and make sure you use Unicode (u'foo') in that method.

Remove prepopulated_fromLink to this heading

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.

Remove coreLink to this heading

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.

Replace class Admin: with admin.pyLink to this heading

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.

ExampleLink to this heading

Below is an example models.py file with all the changes you’ll need to make:

Old (0.96) models.py:

Python
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:

Python
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:

Python
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)

The AdminLink to this heading

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 an 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.

Use new inline syntaxLink to this heading

The new edit_inline options have all been moved to admin.py. Here’s an example:

Old (0.96):

Python
class Parent(models.Model):
    ...

class Child(models.Model):
    parent = models.ForeignKey(Parent, edit_inline=models.STACKED, num_in_admin=3)

New (1.0):

Python
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.

Simplify fields, or use fieldsetsLink to this heading

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):

Python
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):

Python
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'}),
    )

URLsLink to this heading

Update your root urls.pyLink to this heading

If you’re using the admin site, you need to update your root urls.py.

Old (0.96) urls.py:

Python
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:

Python
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 ...
)

WidokiLink to this heading

Use django.forms instead of newformsLink to this heading

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:

Python
from django import newforms as forms

New:

Python
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

Handle uploaded files using the new APILink to this heading

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:

Python
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

Work with file fields using the new APILink to this heading

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.

Use Paginator instead of ObjectPaginatorLink to this heading

The ObjectPaginator in 0.96 has been removed and replaced with an improved version, django.core.paginator.Paginator.

SzablonyLink to this heading

Learn to love autoescapingLink to this heading

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:

Django template
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:

Django template
{% autoescape off %}
   ... unescaped template content here ...
{% endautoescape %}

Less-common changesLink to this heading

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.

SignalsLink to this heading

  • Add **kwargs to any registered signal handlers.

  • Connect, disconnect, and send signals via methods on the Signal object instead of through module methods in django.dispatch.dispatcher.

  • Remove any use of the Anonymous and Any sender options; they no longer exist. You can still receive signals sent by any sender by using sender=None

  • Make any custom signals you’ve declared into instances of 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)

KomentarzeLink to this heading

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.

Tagi szablonuLink to this heading

spaceless tagLink to this heading

The spaceless template tag now removes all spaces between HTML tags, instead of preserving a single space.

Local flavorsLink to this heading

U.S. local flavorLink to this heading

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.

SesjeLink to this heading

Getting a new session keyLink to this heading

SessionBase.get_new_session_key() has been renamed to _get_new_session_key(). get_new_session_object() no longer exists.

FixturesLink to this heading

Loading a row no longer calls save()Link to this heading

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.

UstawieniaLink to this heading

Ulepszone wyjątkiLink to this heading

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ł przeniesionyLink to this heading

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.

Zachowanie APPEND_SLASH zostało zaktualizowaneLink to this heading

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:

Python
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:

Python
r'/some_prefix/(.*/)$'

Mniejsze zmiany modeliLink to this heading

Different exception from get()Link to this heading

Manager-y od teraz zwracają wyjątek MultipleObjectsReturned zamiast AssertionError:

Old (0.96):

Python
try:
    Model.objects.get(...)
except AssertionError:
    handle_the_error()

New (1.0):

Python
try:
    Model.objects.get(...)
except Model.MultipleObjectsReturned:
    handle_the_error()

LazyDate został zwolnionyLink to this heading

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):

Python
class Article(models.Model):
    title = models.CharField(maxlength=100)
    published = models.DateField(default=LazyDate())

New (1.0):

Python
import datetime

class Article(models.Model):
    title = models.CharField(max_length=100)
    published = models.DateField(default=datetime.datetime.now)

Nowe pole DecimalField, a FloatField jest od teraz poprawnym float-em.Link to this heading

Old (0.96):

Python
class MyModel(models.Model):
    field_name = models.FloatField(max_digits=10, decimal_places=3)
    ...

New (1.0):

Python
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.

To upgrade each application to use a DecimalField, you can do the following, replacing <app> in the code below with each app’s name:

Shell
$ ./manage.py dumpdata --format=xml <app> > data-dump.xml
$ ./manage.py reset <app>
$ ./manage.py loaddata data-dump.xml

Notatki:

  1. It’s important that you remember to use XML format in the first step of this process. We are exploiting a feature of the XML data dumps that makes porting floats to decimals with SQLite possible.

  2. In the second step you will be asked to confirm that you are prepared to lose the data for the application(s) in question. Say yes; we’ll restore this data in the third step.

  3. 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.

InternationalizationLink to this heading

django.views.i18n.set_language() now requires a POST requestLink to this heading

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 builtinsLink to this heading

_() (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:

Python
from django.utils.translation import ugettext as _

Obiekty zapytania/odpowiedzi HTTPLink to this heading

Słownikowy dostęp do HttpRequestLink to this heading

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.

Uzyskiwanie dostępu do nagłówków w HTTPResponseLink to this heading

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:.

Relacje generyczneLink to this heading

Relacje generyczne zostały przeniesione poza coreLink to this heading

Klasy relacji generycznych – GenericForeignKey i GenericRelation – zostały przeniesione do modułu django.contrib.contenttypes.

TestowanieLink to this heading

django.test.Client.login() został zmienionyLink to this heading

Old (0.96):

Python
from django.test import Client
c = Client()
c.login('/path/to/login','myuser','mypassword')

New (1.0):

Python
# ... same as above, but then:
c.login(username='myuser', password='mypassword')

Komendy zarządzaniaLink to this heading

Uruchamianie komend zarządzania z Twojego koduLink to this heading

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:

Python
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:

Python
from django.core import management
management.call_command('flush', verbosity=0, interactive=False)
management.call_command('loaddata', 'test_data', verbosity=0)

Subcommands must now precede optionsLink to this heading

django-admin.py and manage.py now require subcommands to precede options. So:

Shell
$ django-admin.py --settings=foo.bar runserver

…no longer works and should be changed to:

Shell
$ django-admin.py runserver --settings=foo.bar

Syndykacja treściLink to this heading

Feed.__init__ has changedLink to this heading

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.

Data structuresLink to this heading

SortedDictFromList is goneLink to this heading

django.newforms.forms.SortedDictFromList was removed. django.utils.datastructures.SortedDict can now be instantiated with a sequence of tuples.

To update your code:

  1. Use django.utils.datastructures.SortedDict wherever you were using django.newforms.forms.SortedDictFromList.

  2. Because 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.

Database backend functionsLink to this heading

Database backend functions have been renamedLink to this heading

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