=========================================Porting your apps from Django 0.96 to 1.0=========================================.. highlight:: pythonDjango 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 ofthis document includes the common changes needed to run with 1.0. If after goingthrough the first part your code still breaks, check the section `Less-commonChanges`_ for a list of a bunch of less-common compatibility issues... seealso::The :doc:`1.0 release notes </releases/1.0>`. That document explains the newfeatures in 1.0 more deeply; the porting guide is more concerned withhelping you quickly update your code.Common changes==============This section describes the changes between 0.96 and 1.0 that most users willneed to make.Use Unicode-----------Change string literals (``'foo'``) into Unicode literals (``u'foo'``). Djangonow uses Unicode strings throughout. In most places, raw strings will continueto work, but updating to use Unicode literals will prevent some obscureproblems.See :doc:`/ref/unicode` for full details.Models------Common changes to your models file:Rename ``maxlength`` to ``max_length``~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Rename your ``maxlength`` argument to ``max_length`` (this was changed to beconsistent with form fields):Replace ``__str__`` with ``__unicode__``~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Replace your model's ``__str__`` function with a ``__unicode__`` method, andmake sure you `use Unicode`_ (``u'foo'``) in that method.Remove ``prepopulated_from``~~~~~~~~~~~~~~~~~~~~~~~~~~~~Remove the ``prepopulated_from`` argument on model fields. It's no longer validand has been moved to the ``ModelAdmin`` class in ``admin.py``. See `theadmin`_, below, for more details about changes to the admin.Remove ``core``~~~~~~~~~~~~~~~Remove the ``core`` argument from your model fields. It is no longernecessary, since the equivalent functionality (part of :ref:`inline editing<admin-inlines>`) is handled differently by the admin interface now. You don'thave 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.py``~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Remove all your inner ``class Admin`` declarations from your models. They won'tbreak anything if you leave them, but they also won't do anything. To registerapps with the admin you'll move those declarations to an ``admin.py`` file;see `the admin`_ below for more details... seealso::A contributor to djangosnippets__ has written a script that'll `scan yourmodels.py and generate a corresponding admin.py`__.__ https://djangosnippets.org/__ https://djangosnippets.org/snippets/603/Example~~~~~~~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 adminfrom models import Authorclass AuthorAdmin(admin.ModelAdmin):list_display = ['first_name', 'last_name']prepopulated_fields = {'slug': ('first_name', 'last_name')}admin.site.register(Author, AuthorAdmin)The Admin---------One of the biggest changes in 1.0 is the new admin. The Django administrativeinterface (``django.contrib.admin``) has been completely refactored; admindefinitions are now completely decoupled from model definitions, the frameworkhas been rewritten to use Django's new form-handling library and redesigned withextensibility 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 ``classAdmin`` with an ``admin.site.register()`` call in an ``admin.py`` file. Below aresome more details on how to rewrite that ``Admin`` declaration into the newsyntax.Use new inline syntax~~~~~~~~~~~~~~~~~~~~~The new ``edit_inline`` options have all been moved to ``admin.py``. Here's anexample: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 = Childextra = 3class ParentAdmin(admin.ModelAdmin):model = Parentinlines = [ChildInline]admin.site.register(Parent, ParentAdmin)See :ref:`admin-inlines` for more details.Simplify ``fields``, or use ``fieldsets``~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~The old ``fields`` syntax was quite confusing, and has been simplified. The oldsyntax 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'}),).. seealso::* More detailed information about the changes and the reasons behind themcan be found on the `NewformsAdminBranch wiki page`__* The new admin comes with a ton of new features; you can read about them inthe :doc:`admin documentation </ref/contrib/admin/index>`.__ https://code.djangoproject.com/wiki/NewformsAdminBranchURLs----Update your root ``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 adminadmin.autodiscover()urlpatterns = patterns('',(r'^admin/(.*)', admin.site.root),# ... the rest of your URLs here ...)Views-----Use ``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 importstatements.Old::from django import newforms as formsNew::from django import formsIf 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 startis the :doc:`forms documentation </topics/forms/index>`Handle uploaded files using the new API~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Replace use of uploaded files -- that is, entries in ``request.FILES`` -- assimple dictionaries with the new:class:`~django.core.files.uploadedfile.UploadedFile`. The old dictionarysyntax 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``===================== =====================Work with file fields using the new API~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~The internal implementation of :class:`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 tomake the following changes, assuming your model's:class:`~django.db.models.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:class:`~django.db.models.ImageField` fields. More details can be found in the:doc:`model API </ref/models/fields>` documentation.Use ``Paginator`` instead of ``ObjectPaginator``~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~The ``ObjectPaginator`` in 0.96 has been removed and replaced with an improvedversion, :class:`django.core.paginator.Paginator`.Templates---------Learn to love autoescaping~~~~~~~~~~~~~~~~~~~~~~~~~~By default, the template system now automatically HTML-escapes the output ofevery variable. To learn more, see :ref:`automatic-html-escaping`.To disable auto-escaping for an individual variable, use the :tfilter:`safe`filter:.. code-block:: html+djangoThis will be escaped: {{ data }}This will not be escaped: {{ data|safe }}To disable auto-escaping for an entire template, wrap the template (or just aparticular section of the template) in the :ttag:`autoescape` tag:.. code-block:: html+django{% autoescape off %}... unescaped template content here ...{% endautoescape %}Less-common changes===================The following changes are smaller, more localized changes. They should onlyaffect more advanced users, but it's probably worth reading through the list andchecking your code for these things.Signals-------* Add ``**kwargs`` to any registered signal handlers.* Connect, disconnect, and send signals via methods on the:class:`~django.dispatch.Signal` object instead of through module methods in``django.dispatch.dispatcher``.* Remove any use of the ``Anonymous`` and ``Any`` sender options; they no longerexist. You can still receive signals sent by any sender by using``sender=None``* Make any custom signals you've declared into instances of:class:`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)``================================================= ======================================Comments--------If you were using Django 0.96's ``django.contrib.comments`` app, you'll need toupgrade to the new comments app introduced in 1.0. See the upgrade guidefor details.Template tags-------------:ttag:`spaceless` tag~~~~~~~~~~~~~~~~~~~~~The ``spaceless`` template tag now removes *all* spaces between HTML tags,instead of preserving a single space.Local flavors-------------U.S. local flavor~~~~~~~~~~~~~~~~~``django.contrib.localflavor.usa`` has been renamed to``django.contrib.localflavor.us``. This change was made to match the namingscheme of other local flavors. To migrate your code, all you need to do ischange the imports.Sessions--------Getting a new session key~~~~~~~~~~~~~~~~~~~~~~~~~``SessionBase.get_new_session_key()`` has been renamed to``_get_new_session_key()``. ``get_new_session_object()`` no longer exists.Fixtures--------Loading a row no longer calls ``save()``~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Previously, loading a row automatically ran the model's ``save()`` method. Thisis no longer the case, so any fields (for example: timestamps) that wereauto-populated by a ``save()`` now need explicit values in any fixture.Settings--------Better exceptions~~~~~~~~~~~~~~~~~The old :exc:`EnvironmentError` has split into an:exc:`ImportError` when Django fails to find the settings moduleand a :exc:`RuntimeError` when you try to reconfigure settingsafter having already used them.:setting:`LOGIN_URL` has moved~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~The :setting:`LOGIN_URL` constant moved from ``django.contrib.auth`` into the``settings`` module. Instead of using ``from django.contrib.auth importLOGIN_URL`` refer to :setting:`settings.LOGIN_URL <LOGIN_URL>`.:setting:`APPEND_SLASH` behavior has been updated~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~In 0.96, if a URL didn't end in a slash or have a period in the finalcomponent of its path, and :setting:`APPEND_SLASH` was True, Django wouldredirect to the same URL, but with a slash appended to the end. Now, Djangochecks to see whether the pattern without the trailing slash would be matchedby something in your URL patterns. If so, no redirection takes place, becauseit is assumed you deliberately wanted to catch that pattern.For most people, this won't require any changes. Some people, though, have URLpatterns that look like this::r'/some_prefix/(.*)$'Previously, those patterns would have been redirected to have a trailingslash. If you always want a slash on such URLs, rewrite the pattern as::r'/some_prefix/(.*/)$'Smaller model changes---------------------Different exception from ``get()``~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Managers now return a :exc:`~django.core.exceptions.MultipleObjectsReturned`exception instead of :exc:`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`` has been fired~~~~~~~~~~~~~~~~~~~~~~~~~~~The ``LazyDate`` helper class no longer exists.Default field values and query arguments can both be callable objects, soinstances of ``LazyDate`` can be replaced with a reference to ``datetime.datetime.now``:Old (0.96)::class Article(models.Model):title = models.CharField(maxlength=100)published = models.DateField(default=LazyDate())New (1.0)::import datetimeclass Article(models.Model):title = models.CharField(max_length=100)published = models.DateField(default=datetime.datetime.now)``DecimalField`` is new, and ``FloatField`` is now a proper float~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~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)...If you forget to make this change, you will see errors about ``FloatField``not taking a ``max_digits`` attribute in ``__init__``, because the new``FloatField`` takes no precision-related arguments.If you're using MySQL or PostgreSQL, no further changes are needed. Thedatabase column types for ``DecimalField`` are the same as for the old``FloatField``.If you're using SQLite, you need to force the database to view theappropriate columns as decimal types, rather than floats. To do this, you'llneed to reload your data. Do this after you have made the change to using``DecimalField`` in your code and updated the Django code... warning::**Back up your database first!**For SQLite, this means making a copy of the single file that stores thedatabase (the name of that file is the ``DATABASE_NAME`` in your``settings.py`` file).To upgrade each application to use a ``DecimalField``, you can do thefollowing, replacing ``<app>`` in the code below with each app's name:.. code-block:: console$ ./manage.py dumpdata --format=xml <app> > data-dump.xml$ ./manage.py reset <app>$ ./manage.py loaddata data-dump.xmlNotes:1. It's important that you remember to use XML format in the first step ofthis process. We are exploiting a feature of the XML data dumps that makesporting floats to decimals with SQLite possible.2. In the second step you will be asked to confirm that you are prepared tolose the data for the application(s) in question. Say yes; we'll restorethis data in the third step.3. ``DecimalField`` is not used in any of the apps shipped with Django priorto this change being made, so you do not need to worry about performingthis procedure for any of the standard Django models.If something goes wrong in the above process, just copy your backed updatabase file over the original file and start again.Internationalization--------------------:func:`django.views.i18n.set_language` now requires a POST request~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Previously, a GET request was used. The old behavior meant that state (thelocale used to display the site) could be changed by a GET request, which isagainst the HTTP specification's recommendations. Code calling this view mustensure that a POST request is now made, instead of a GET. This means you canno longer use a link to access the view, but must use a form submission ofsome kind (e.g. a button).``_()`` is no longer in builtins~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~``_()`` (the callable object whose name is a single underscore) is no longermonkeypatched into builtins -- that is, it's no longer available magically inevery module.If you were previously relying on ``_()`` always being present, you should nowexplicitly import ``ugettext`` or ``ugettext_lazy``, if appropriate, and aliasit to ``_`` yourself::from django.utils.translation import ugettext as _HTTP request/response objects-----------------------------Dictionary access to ``HttpRequest``~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~``HttpRequest`` objects no longer directly support dictionary-styleaccess; previously, both ``GET`` and ``POST`` data were directlyavailable on the ``HttpRequest`` object (e.g., you could check for apiece of form data by using ``if 'some_form_key' in request`` or byreading ``request['some_form_key']``. This is no longer supported; ifyou need access to the combined ``GET`` and ``POST`` data, use``request.REQUEST`` instead.It is strongly suggested, however, that you always explicitly look inthe appropriate dictionary for the type of request you expect toreceive (``request.GET`` or ``request.POST``); relying on the combined``request.REQUEST`` dictionary can mask the origin of incoming data.Accessing ``HTTPResponse`` headers~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~``django.http.HttpResponse.headers`` has been renamed to ``_headers`` and:class:`~django.http.HttpResponse` now supports containment checking directly.So use ``if header in response:`` instead of ``if header in response.headers:``.Generic relations-----------------Generic relations have been moved out of core~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~The generic relation classes -- ``GenericForeignKey`` and ``GenericRelation``-- have moved into the :mod:`django.contrib.contenttypes` module.Testing-------:meth:`django.test.Client.login` has changed~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Old (0.96)::from django.test import Clientc = Client()c.login('/path/to/login','myuser','mypassword')New (1.0)::# ... same as above, but then:c.login(username='myuser', password='mypassword')Management commands-------------------Running management commands from your code~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~:mod:`django.core.management` has been greatly refactored.Calls to management services in your code now need to use``call_command``. For example, if you have some test code that calls flush andload_data::from django.core import managementmanagement.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 managementmanagement.call_command('flush', verbosity=0, interactive=False)management.call_command('loaddata', 'test_data', verbosity=0)Subcommands must now precede options~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~``django-admin.py`` and ``manage.py`` now require subcommands to precedeoptions. So:.. code-block:: console$ django-admin.py --settings=foo.bar runserver...no longer works and should be changed to:.. code-block:: console$ django-admin.py runserver --settings=foo.barSyndication-----------``Feed.__init__`` has changed~~~~~~~~~~~~~~~~~~~~~~~~~~~~~The ``__init__()`` method of the syndication framework's ``Feed`` class nowtakes an ``HttpRequest`` object as its second parameter, instead of the feed'sURL. This allows the syndication framework to work without requiring the sitesframework. This only affects code that subclasses ``Feed`` and overrides the``__init__()`` method, and code that calls ``Feed.__init__()`` directly.Data structures---------------``SortedDictFromList`` is gone~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~``django.newforms.forms.SortedDictFromList`` was removed.``django.utils.datastructures.SortedDict`` can now be instantiated witha sequence of tuples.To update your code:1. Use ``django.utils.datastructures.SortedDict`` wherever you wereusing ``django.newforms.forms.SortedDictFromList``.2. Because ``django.utils.datastructures.SortedDict.copy`` doesn'treturn a deepcopy as ``SortedDictFromList.copy()`` did, you will needto update your code if you were relying on a deepcopy. Do this by using``copy.deepcopy`` directly.Database backend functions--------------------------Database backend functions have been renamed~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Almost *all* of the database backend-level functions have been renamed and/orrelocated. None of these were documented, but you'll need to change your codeif you're using any of these functions, all of which are in :mod:`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``======================================= ===================================================