From 1156db790a27975108cb78cd49f0ee549a7da739 Mon Sep 17 00:00:00 2001 From: Jacob Kaplan-Moss Date: Tue, 2 Sep 2008 16:27:47 +0000 Subject: [PATCH] Added a rough draft of the 1.0 porting guide, and an obviously incomplete placeholder for the 1.0 release notes. Thanks to daonb, patrickf, and ramiro for contributions to this porting guide. Refs #8438. git-svn-id: http://code.djangoproject.com/svn/django/trunk@8856 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- docs/ref/contrib/admin.txt | 2 + docs/releases/1.0-porting-guide.txt | 691 ++++++++++++++++++++++++++++ docs/releases/1.0.txt | 19 + docs/releases/index.txt | 5 +- 4 files changed, 715 insertions(+), 2 deletions(-) create mode 100644 docs/releases/1.0-porting-guide.txt create mode 100644 docs/releases/1.0.txt diff --git a/docs/ref/contrib/admin.txt b/docs/ref/contrib/admin.txt index e2bd39c686..645cc02916 100644 --- a/docs/ref/contrib/admin.txt +++ b/docs/ref/contrib/admin.txt @@ -632,6 +632,8 @@ It is important you use a ``ModelForm`` here otherwise things can break. See the :ref:`forms ` documentation on :ref:`custom validation ` for more information. +.. _admin-inlines: + ``InlineModelAdmin`` objects ============================ diff --git a/docs/releases/1.0-porting-guide.txt b/docs/releases/1.0-porting-guide.txt new file mode 100644 index 0000000000..6380129e93 --- /dev/null +++ b/docs/releases/1.0-porting-guide.txt @@ -0,0 +1,691 @@ +========================================= +Porting your apps from Django 0.96 to 1.0 +========================================= + +.. highlight:: python + +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. + +.. seealso:: + + The :ref:`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. + +Common changes +============== + +This section describes the changes between 0.96 and 1.0 that most users will +need to make. + +Use Unicode +----------- + +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 :ref:`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 be +consistant with form fields): + +Replace ``__str__`` with ``__unicode__`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Replace your model's ``__str__`` function with a ``__unicode__`` method, and +make sure you `use Unicode`_ (``u'foo'``) in that method. + +Remove ``prepopulated_from`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Remove the ``prepopulated_from`` argument on model fields. It's no longer valid +and has been moved to the ``AdminModel`` class in ``admin.py``. See `the +admin`_, below, for more details about changes to the admin. + +Replace ``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. + +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'] + +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) + +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) + +The Admin +--------- + +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 +as been rewritten to use Django's new form-handling library and redesigned with +extensibility and customization in mind. + +Practially, 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. + +.. seealso:: + + A contributor to djangosnippets__ has written a script that'll `scan your + models.py and generate a corresponding admin.py`__. + + __ http://www.djangosnippets.org/ + __ http://www.djangosnippets.org/snippets/603/ + +Use new inline 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(models.ModelAdmin): + model = Parent + inlines = [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 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'}), + ) + + +.. seealso:: + + * More detailed information about the changes and the reasons behind them + can be found on the `NewformsAdminBranch wiki page`__ + + * The new admin comes with a ton of new features; you can read about them in + the :ref:`admin documentation `. + + __ http://code.djangoproject.com/wiki/NewformsAdminBranch + +URLS +---- + +Update your root ``urls.py`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you are 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 ... + ) + +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 removed. + +If you are already using new forms all you have to do is change your import +statement. Instead of ``from django import newforms as forms``, use ``from +django import forms``. + +If you are using the old forms system, you will have to rewrite your forms. A +good place to start is the :ref:`forms documentation ` + +Handle uploaded files using the new API +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Replace use of uploaded files -- that is, entries in ``request.FILES`` -- as +simple dictionaries with the new :class:`~django.core.files.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`` +===================== ===================== + +Templates +--------- + +Learn to love autoescaping +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +By default, the templating system now automatically HTML-escapes the output of +every 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+django + + 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 :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 only +affect more advanced users, but it's probably worth reading through the list and +checking 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 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 + :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)`` +================================================= ====================================== + +Template tags +------------- + +:ttag:`spaceless` tag +~~~~~~~~~~~~~~~~~~~~~ + +The spaceless template tag removes *all* spaces between HTML tags instead of +preserving a single space. + +Localflavor +----------- + +US localflavor +~~~~~~~~~~~~~~ + +``django.contrib.localflavor.usa`` has been renamed +:mod:`django.contribg.localflavor.us`. This change was made to match the naming +scheme of other local flavors. + +Sessions +-------- + +Getting a new session key +~~~~~~~~~~~~~~~~~~~~~~~~~ + +``SeesionBase.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. 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. + +Settings +-------- + +Better exceptions +~~~~~~~~~~~~~~~~~ + +The old :exc:`EnvironmentError` was split into an :exc:`ImportError` raised when +Django fails to find the settings module and a :exc:`RuntimeError` when you try +to reconfigure settings after having already used them + +``LOGIN_URL`` has moved +~~~~~~~~~~~~~~~~~~~~~~~ + +The ``LOGIN_URL`` constant moved from ``django.contrib.auth`` into the +``settings`` module. Instead of using ``from django.contrib.auth import +LOGIN_URL`` refer to :setting:`settings.LOGIN_URL `. + +:setting:`APPEND_SLASH` behaviour has been updated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In 0.96, if a URL didn't end in a slash or have a period in the final +component of it's 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 if 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. + +For most people, this won't require any changes. Some people, though, have URL +patterns that look like this:: + + r'/some_prefix/(.*)$' + +Previously, those patterns would have been redirected to have a trailing +slash. If you always want a slash on such URLs, rewrite the pattern as:: + + r'/some_prefix/(.*/)$' + +Samller model changes +--------------------- + +Different exception from ``get()`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The models manager now returns a :exc:`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, so +instances 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 datetime + + class Article(models.Model): + title = models.CharField(maxlength=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__``, since the new +``FloatField`` takes no precision-related arguments. + +If you are using MySQL or PostgreSQL, there are no further changes needed. The +database column types for ``DecimalField`` are the same as for the old +``FloatField``. + +If you are using SQLite, you need to force the database to view the +appropriate columns as decimal types, rather than floats. To do this, you'll +need 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 the + database (the name of that file is the ``DATABASE_NAME`` in your settings.py + file). + +To upgrade each application to use a ``DecimalField``, do the following, +replacing ```` in the code below with each app's name: + +.. code-block:: bash + + $ ./manage.py dumpdata --format=xml > data-dump.xml + $ ./manage.py reset + $ ./manage.py loaddata data-dump.xml + +Notes: + + 1. It is 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, of course. + + 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 top of 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 (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 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``_()`` is no longer monkeypatched into builtins. 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 _ + +HTTP request/response objects +----------------------------- + +Accessing ``HTTPResponse`` headers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``django.http.HttpResponse.headers`` has been renamed to ``_headers`` and +:class:`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 Client + c = 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 greately refactored. + +Calls to management services in your code will 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 will 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) + +Subcommands must now preceed options +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``django-admin.py`` and ``manage.py`` now require subcommands to precede +options. So: + +.. code-block:: bash + + $ django-admin.py --settings=foo.bar runserver + +no longer works, and must be changed to: + +.. code-block:: bash + + $ django-admin.py runserver --settings=foo.bar + +Syndication +----------- + +``Feed.__init__`` has changed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``__init__()`` parameters in in syndication framework's ``Feed`` class now +take 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 subclass ``Feed`` and overrides the +``__init__()`` method, and code that calls ``Feed.__init__()`` directly. + +Data structures +--------------- + +``SortedDictFromList`` is gone +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`django.newforms.forms.SortedDictFromList`` was removed. +:class:`django.utils.datastructures.SortedDict`` can now be instantiated with +a sequence of tuples. + +To update your code: + + 1. Use :class:`django.utils.datastructures.SortedDict` wherever you were + using ``django.newforms.forms.SortedDictFromList``. + + 2. Since :meth:`django.utils.datastructures.SortedDict.copy` return a + deepcopy as ``SortedDictFromList`` method did, you will need to 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/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 :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`` +======================================= =================================================== + diff --git a/docs/releases/1.0.txt b/docs/releases/1.0.txt new file mode 100644 index 0000000000..58b3193335 --- /dev/null +++ b/docs/releases/1.0.txt @@ -0,0 +1,19 @@ +.. _releases-1.0: + +======================== +Django 1.0 release notes +======================== + +Welcome to Django 1.0! + +Porting guide +------------- + +You can find detailed instructions on porting apps from Django 0.96 to Django +1.0 in our porting guide: + +.. toctree:: + :maxdepth: 1 + + 1.0-porting-guide + diff --git a/docs/releases/index.txt b/docs/releases/index.txt index 613fd3fa7a..e65c8001d3 100644 --- a/docs/releases/index.txt +++ b/docs/releases/index.txt @@ -9,13 +9,14 @@ changes made in that version. .. toctree:: :maxdepth: 1 - + 0.95 0.96 1.0-alpha-1 1.0-alpha-2 1.0-beta 1.0-beta-2 + 1.0 .. seealso:: @@ -23,5 +24,5 @@ changes made in that version. development "trunk". If you're running versions of Django newer than an official release, you should keep track of new pieces pointed there. It's also fun reading if you're looking forward to new versions of Django. - + .. _backwards-incompatible changes: http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges