Changes between Version 78 and Version 79 of BackwardsIncompatibleChanges


Ignore:
Timestamp:
Mar 28, 2007, 9:47:34 PM (17 years ago)
Author:
Malcolm Tredinnick
Comment:

Keep the page length manageable by moving older changes to another page.

Legend:

Unmodified
Added
Removed
Modified
  • BackwardsIncompatibleChanges

    v78 v79  
    55Of course, once we reach 1.0, we'll be strongly committed to backward compatibility.
    66
    7 This page lists all backwards-incompatible changes to Django so far.
     7This page lists all backwards-incompatible changes to Django since the 0.95 release. For changes prior to 0.95, see the OlderBackwardsIncompatibleChanges page.
    88
    99== Table of Contents ==
    10 
    11 Old-school Django (Pre-0.90)
    12  * [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#Movedmod_pythonhandler Moved mod_python handler]
    13  * [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#Changedorderingsyntax Changed ordering syntax]
    14  * [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#Refactoredmeta.py Refactored meta.py]
    15  * [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#Changededit_inlineandedit_inline_typebehavior Changed edit_inline and edit_inline_type behavior]
    16  * [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#ChangedadminlogtostoreprimarykeysasTEXTfieldsnotINTEGERfields Changed admin log to store primary keys as TEXT fields, not INTEGER fields]
    17  * [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#Addedsupportforanonymoussessions Added support for anonymous sessions]
    18  * [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#Changeyoursettingsfiles Change your settings files]
    19  * [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#Createthecore_sessionsdatabasetable Create the core_sessions database table]
    20  * [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#Removeoldunneededthings Remove old, unneeded things]
    21  * [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#Changedmodelsyntax Changed model syntax]
    22  * [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#Movedtemplateloadermodule Moved template loader module]
    23  * [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#Refactoredtheadminappnottorequireitsownsettingsfile Refactored the admin app not to require its own settings file]
    24  * [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#Separatedflatpagesandredirectsintostandaloneoptionalapps Separated flatpages and redirects into standalone, optional apps]
    25  * [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#RefactoredRSSframework Refactored RSS framework]
    26 
    27 Changes made after Django [source:/django/tags/releases/0.90 0.90]
    28  * Nov. 20: [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#Changedfieldnameandlengthforauth.Userpassword_md5field Changed field name and length for auth.User password_md5 field]
    29  * Nov. 26: [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#Templatesystemchanges Template system changes]
    30  * Nov. 27: [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#AddedsupportfornonnamedgroupsinURLconfregularexpressions Added support for non-named groups in URLconf regular expressions]
    31  * Nov. 29: [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#ChangedbehaviorofMultiValueDict.items Changed behavior of MultiValueDict.items()]
    32  * Nov. 30: [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#Removedundocumented_ordatabaseAPIparameter Removed undocumented '_or' database API parameter]
    33 
    34 Changes made after Django [source:/django/tags/releases/0.91 0.91]
    35  * May 1, 2006: [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#Removedthemagic Removed the magic]
    3610
    3711Changes made after Django [source:/django/tags/releases/0.95 0.95]
     
    4418 * March 14, 2007: [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#EnforcingMySQLdbversion Enforcing MySQLdb version]
    4519
    46 
    47 == Moved mod_python handler ==
    48 
    49 As of [169], using {{{django.core.handler}}} as a mod_python handler is deprecated. Use {{{django.core.handlers.modpython}}} instead. We will be removing {{{django.core.handler}}} for 1.0.
    50 
    51 == Changed ordering syntax ==
    52 
    53 As of [292], syntax used for {{{order_by}}} (in the database API) and {{{ordering}}} (in models) has changed.
    54 
    55 Example of old ordering syntax:
    56 {{{order_by=[('foo', 'ASC'), ('bar', 'DESC')]}}}
    57 
    58 Example of new ordering syntax:
    59 {{{order_by=['foo', '-bar']}}}
    60 
    61 The old syntax is deprecated, and we'll stop supporting it for 1.0.
    62 
    63 == Refactored meta.py ==
    64 
    65 As of [378], {{{django/core/meta.py}}} has been converted to a package, {{{django/core/meta/}}}. If you're using a version of Django from before [378], make sure to delete {{{django/core/meta.pyc}}} and {{{django/core/meta.pyo}}}, if they exist. The existence of those files doesn't pose any known problems, but it's best to clean things up.
    66 
    67 == Changed edit_inline and edit_inline_type behavior ==
    68 
    69 As of [440], using {{{edit_inline_type}}} in your models is deprecated, in favor of a less-redundant approach that uses {{{edit_inline}}} itself.
    70 
    71 Example of old syntax:
    72 {{{edit_inline=True, edit_inline_type=meta.TABULAR}}}
    73 
    74 Example of new syntax:
    75 {{{edit_inline=meta.TABULAR}}}
    76 
    77 We'll stop supporting the old syntax for 1.0.
    78 
    79 == Changed admin log to store primary keys as TEXT fields, not INTEGER fields ==
    80 
    81 As of [469], the {{{object_id}}} field in {{{django.models.auth.LogEntry}}} is a {{{TextField}}} instead of an {{{IntegerField}}}. We made this change to accomodate non-integer primary keys.
    82 
    83 If you're using a Django database installation from before [469] and you want to use non-integer primary keys on an object you edit in the admin site, you'll need to do an {{{ALTER TABLE}}} in your database.
    84 
    85 In PostgreSQL:
    86 
    87 {{{
    88 BEGIN;
    89 ALTER TABLE auth_admin_log RENAME object_id TO object_id_old;
    90 ALTER TABLE auth_admin_log ADD COLUMN object_id TEXT;
    91 UPDATE auth_admin_log SET object_id = object_id_old;
    92 ALTER TABLE auth_admin_log DROP COLUMN object_id_old;
    93 COMMIT;
    94 }}}
    95 
    96 In MySQL:
    97 
    98 {{{
    99 ALTER TABLE auth_admin_log MODIFY object_id TEXT;
    100 }}}
    101 
    102 == Added support for anonymous sessions ==
    103 
    104 As of [518], Django has support for anonymous sessions. If you're using a Django database installation from before [518] and you want to use the Django admin, anonymous sessions or auth-based sessions, you'll need to make a few updates to your database and settings files.
    105 
    106 === Change your settings files ===
    107 
    108 Add {{{"django.middleware.sessions.SessionMiddleware"}}} to the {{{MIDDLEWARE_CLASSES}}} tuple in your admin settings file. Make sure it appears before {{{"django.middleware.admin.AdminUserRequired"}}}. (The middleware classes are applied in order, and the admin middleware requires that the session middleware come first.)
    109 
    110 If you want session support any other (i.e., non-admin) Django installation, change the {{{MIDDLEWARE_CLASSES}}} setting accordingly. The order (i.e., whether it comes before or after the other installed middleware classes) doesn't matter.
    111 
    112 === Create the core_sessions database table ===
    113 
    114 In PostgreSQL, use this:
    115 
    116 {{{
    117 CREATE TABLE core_sessions (
    118     session_key varchar(40) NOT NULL PRIMARY KEY,
    119     session_data text NOT NULL,
    120     expire_date timestamp with time zone NOT NULL
    121 );
    122 INSERT INTO content_types (name, package, python_module_name) VALUES ('session', 'core', 'sessions');
    123 }}}
    124 
    125 In MySQL and SQLite, use this:
    126 
    127 {{{
    128 CREATE TABLE core_sessions (
    129     session_key varchar(40) NOT NULL PRIMARY KEY,
    130     session_data text NOT NULL,
    131     expire_date datetime NOT NULL
    132 );
    133 INSERT INTO content_types (name, package, python_module_name) VALUES ('session', 'core', 'sessions');
    134 }}}
    135 
    136 === Remove old, unneeded things ===
    137 
    138 Execute this SQL in your database:
    139 
    140 {{{
    141 DROP TABLE auth_sessions;
    142 DELETE FROM content_types WHERE package = 'auth' AND python_module_name = 'sessions';
    143 }}}
    144 
    145 Edit your settings file(s) to remove {{{AUTH_SESSION_COOKIE}}} and {{{REGISTRATION_COOKIE_DOMAIN}}}, if they exist.
    146 
    147 == Changed model syntax ==
    148 
    149 As of [549], Django's model syntax has changed. If you're using models that use old (pre-[549]) syntax, you'll need to convert them according to the instructions on ModelSyntaxChangeInstructions.
    150 
    151 == Moved template loader module ==
    152 
    153 As of [867], {{{django.core.template_loader}}} is deprecated. Use {{{django.core.template.loader}}} instead.
    154 
    155 == Refactored the admin app not to require its own settings file ==
    156 
    157 As of [948], the admin has been refactored, to make things simpler and tighter -- both conceptually and in code layout.
    158 
    159 === What changed ===
    160 
    161  * The admin no longer requires its own settings file. The "main" site and admin site can run on the same Django installation.
    162  * All the admin code moved to {{{django/contrib/admin}}}.
    163  * The admin requires {{{"django.contrib.admin"}}} in {{{INSTALLED_APPS}}}, and it requires the {{{app_directories}}} [http://www.djangoproject.com/documentation/templates_python/#loader-types template loader].
    164  * The admin database table isn't installed unless you explicitly have the admin installed ({{{django-admin.py install admin}}}).
    165  * Renamed the admin log database table to give it a {{{"django"}}} prefix.
    166 
    167 === How to update your code ===
    168 
    169 If you're using a Django installation from before this changeset, do the following to restore your admin site:
    170 
    171  * Execute this SQL command: {{{ALTER TABLE auth_admin_log RENAME TO django_admin_log;}}}
    172  * If you're using an SQLite version older than 3.2.0 (no ALTER TABLE support), execute these SQL commands (following [http://www.sqlite.org/faq.html#q13 this pattern]):
    173    {{{
    174    BEGIN TRANSACTION;
    175    CREATE TEMPORARY TABLE auth_backup (
    176     id integer NOT NULL PRIMARY KEY,
    177     action_time datetime NOT NULL,
    178     user_id integer NOT NULL REFERENCES auth_users (id),
    179     content_type_id integer NULL REFERENCES content_types (id),
    180     object_id text NULL,
    181     object_repr varchar(200) NOT NULL,
    182     action_flag smallint unsigned NOT NULL,
    183     change_message text NOT NULL
    184    );
    185    INSERT INTO auth_backup SELECT id, action_time, user_id, content_type_id, object_id, object_repr, action_flag, change_message FROM auth_admin_log;
    186    DROP TABLE auth_admin_log;
    187    CREATE TABLE django_admin_log (
    188     id integer NOT NULL PRIMARY KEY,
    189     action_time datetime NOT NULL,
    190     user_id integer NOT NULL REFERENCES auth_users (id),
    191     content_type_id integer NULL REFERENCES content_types (id),
    192     object_id text NULL,
    193     object_repr varchar(200) NOT NULL,
    194     action_flag smallint unsigned NOT NULL,
    195     change_message text NOT NULL
    196    );
    197    INSERT INTO django_admin_log SELECT id, action_time, user_id, content_type_id, object_id, object_repr, action_flag, change_message FROM auth_backup;
    198    DROP TABLE auth_backup;
    199    COMMIT;
    200    }}}
    201  * If you're using PostgreSQL, execute these SQL commands:
    202    {{{
    203    ALTER TABLE auth_admin_log_id_seq RENAME TO django_admin_log_id_seq;
    204    ALTER TABLE django_admin_log ALTER COLUMN id DROP DEFAULT;
    205    ALTER TABLE django_admin_log ALTER COLUMN id SET DEFAULT nextval('public.django_admin_log_id_seq'::text);
    206    }}}
    207  * Edit your Django settings file (probably called {{{settings/main.py}}}) to make the following changes:
    208    * Add {{{"django.contrib.admin"}}} to {{{INSTALLED_APPS}}}. Order doesn't matter; it can be the first, last, whatever.
    209    * Remove {{{"django.middleware.admin.AdminUserRequired"}}} from {{{MIDDLEWARE_CLASSES}}}, if it's in there.
    210    * Add {{{"django.middleware.sessions.SessionMiddleware"}}} to {{{MIDDLEWARE_CLASSES}}}, if it's not already in there.
    211    * If you've created any custom admin templates, add the appropriate line from the admin {{{TEMPLATE_DIRS}}} setting to your "main" {{{TEMPLATE_DIRS}}}.
    212      * (Note that Django looks for "404.html" and "500.html" templates in your {{{TEMPLATE_DIRS}}}. Make sure you have these templates available.)
    213    * If you've created any custom admin templates, note that the template inheritance structure has changed. All admin templates are now within an {{{admin}}} subdirectory of the template directory. The following admin templates are directly affected by this change:
    214      * 404 --> admin/404
    215      * 500 --> admin/500
    216      * base --> admin/base
    217      * base_site --> admin/base_site
    218      * admin_object_history --> admin/object_history
    219      * delete_confirmation_generic --> admin/delete_confirmation
    220      * index --> admin/index
    221      * login --> admin/login
    222      * template_validator --> admin/template_validator
    223    * Add {{{"django.core.template.loaders.app_directories.load_template_source"}}} to {{{TEMPLATE_LOADERS}}}, after {{{"django.core.template.loaders.filesystem.load_template_source"}}}. If you don't have the {{{TEMPLATE_LOADERS}}} setting, set it to this:
    224      {{{
    225      TEMPLATE_LOADERS = (
    226          'django.core.template.loaders.filesystem.load_template_source',
    227          'django.core.template.loaders.app_directories.load_template_source',
    228      )
    229      }}}
    230 
    231  * Remove your admin settings file (probably called {{{settings/admin.py}}}) and admin URLconf (probably called {{{settings/urls/admin.py}}}).
    232  * Delete {{{django/templatetags/*.pyc}}} and {{{django/templatetags/*.pyo}}}, just to be safe.
    233  * Edit your main URLconf (probably called {{{settings/urls/main.py}}}) and add this line:
    234 
    235      {{{
    236      (r'^admin/', include('django.contrib.admin.urls.admin')),
    237      }}}
    238 
    239    Change that {{{"admin"}}} to whatever URL you were using for the admin site.
    240 
    241 If you use external mapping to files and directories (e.g., using mod_rewrite), do the following:
    242 
    243  * Make sure you're not redirecting {{{/admin}}} to another instance of Django with different settings (e.g., using settings/admin.py).
    244  * If you had a mapping of admin media files, make sure that it points to new directory, which is {{{/your/path/to/django/contrib/admin/media}}}.
    245 
    246 The following steps are optional but will tighten your code up. All assume your project is called {{{myproject}}}.
    247 
    248  * Move {{{myproject/settings/urls/main.py}}} to {{{myproject/urls.py}}}.
    249  * Delete {{{myproject/settings/urls/admin.py}}} (unless you had custom things in it, of course).
    250  * Move {{{myproject/settings/main.py}}} to {{{myproject/settings.py}}}.
    251  * Edit {{{myproject/settings.py}}} to change {{{ROOT_URLCONF}}} from {{{"myproject.settings.urls.main"}}} to {{{"myproject.urls"}}}.
    252  * If you use {{{myproject/settings/main_rss.py}}} to describe your RSS feeds, move it to {{{myproject/settings_rss.py}}}.
    253  * Change {{{DJANGO_SETTINGS_MODULE}}} in Apache configuration from {{{"myproject.settings.main"}}} to {{{"myproject.settings"}}}.
    254  * Having done all this, delete the directory {{{myproject/settings}}}.
    255 
    256 == Separated flatpages and redirects into standalone, optional apps ==
    257 
    258 As of [1166], flatpages and redirects, previously installed by default, are now optional add-ons. Now they must be installed manually.
    259 
    260 === What changed ===
    261 
    262  * The flatpages and redirects database files are no longer installed by default.
    263  * Renamed all references to "flatfiles" -- a previous name for flatpages -- to "flatpages", to be unambiguous.
    264  * Renamed the flatpages and redirects database tables.
    265  * Moved all flatpages and redirects logic from throughout the Django code to {{{django.contrib}}}.
    266  * To use flatpages and redirects, you now need to specify them in {{{INSTALLED_APPS}}} -- use {{{"django.contrib.flatpages"}}} and {{{"django.contrib.redirects"}}}.
    267 
    268 === How to update your code ===
    269 
    270 If you're using a Django installation from before this changeset, do the following to restore flatpages and redirects functionality:
    271 
    272 ==== If you don't want to use flatfiles ====
    273 
    274 Execute the following SQL:
    275 
    276 {{{
    277 DROP TABLE flatfiles;
    278 DROP TABLE flatfiles_sites;
    279 DELETE FROM auth_permissions WHERE package = 'core' AND codename IN ('add_flatfile', 'change_flatfile', 'delete_flatfile');
    280 DELETE FROM content_types WHERE package = 'core' AND python_module_name = 'flatfiles';
    281 }}}
    282 
    283 ==== If you don't want to use redirects ====
    284 
    285 Execute the following SQL:
    286 
    287 {{{
    288 DROP TABLE redirects;
    289 DELETE FROM auth_permissions WHERE package = 'core' AND codename IN ('add_redirect', 'change_redirect', 'delete_redirect');
    290 DELETE FROM content_types WHERE package = 'core' AND python_module_name = 'redirects';
    291 }}}
    292 
    293 ==== If you want to use flatfiles ====
    294 
    295  * If you're using PostgreSQL, execute the following:
    296    {{{
    297    ALTER TABLE flatfiles RENAME TO django_flatpages;
    298    ALTER TABLE flatfiles_sites RENAME TO django_flatpages_sites;
    299    ALTER TABLE django_flatpages_sites RENAME flatfile_id TO flatpage_id;
    300    INSERT INTO packages (label, name) VALUES ('flatpages', 'flatpages');
    301    UPDATE content_types SET package = 'flatpages', python_module_name = 'flatpages' WHERE package = 'core' AND python_module_name = 'flatfiles';
    302    UPDATE auth_permissions SET package = 'flatpages' WHERE package = 'core' AND codename IN ('add_flatfile', 'change_flatfile', 'delete_flatfile');
    303    ALTER TABLE flatfiles_id_seq RENAME TO django_flatpages_id_seq;
    304    ALTER TABLE django_flatpages ALTER COLUMN id DROP DEFAULT;
    305    ALTER TABLE django_flatpages ALTER COLUMN id SET DEFAULT nextval('public.django_flatpages_id_seq'::text);
    306    }}}
    307  * If you're using MySQL, execute the following:
    308    {{{
    309    ALTER TABLE flatfiles RENAME TO django_flatpages;
    310    ALTER TABLE flatfiles_sites RENAME TO django_flatpages_sites;
    311    ALTER TABLE django_flatpages_sites CHANGE flatfile_id flatpage_id INTEGER;
    312    INSERT INTO packages (label, name) VALUES ('flatpages', 'flatpages');
    313    UPDATE content_types SET package = 'flatpages', python_module_name = 'flatpages' WHERE package = 'core' AND python_module_name = 'flatfiles';
    314    UPDATE auth_permissions SET package = 'flatpages' WHERE package = 'core' AND codename IN ('add_flatfile', 'change_flatfile', 'delete_flatfile');
    315    }}}
    316  * If you have a {{{flatfiles/default.html}}} template, rename it to {{{flatpages/default.html}}}.
    317  * In every one of your flatpage templates, change the variable {{{flatfile}}} to {{{flatpage}}}.
    318  * If you use the URLconf {{{django.conf.urls.flatfiles}}}, now point to {{{django.contrib.flatpages.urls}}}.
    319  * If you have a {{{USE_FLAT_PAGES}}} setting, remove it.
    320  * Add {{{"django.contrib.flatpages"}}} to your {{{INSTALLED_APPS}}}.
    321  * Add {{{"django.contrib.flatpages.middleware.FlatpageFallbackMiddleware"}}} to your {{{MIDDLEWARE_CLASSES}}}.
    322 
    323 ==== If you want to use redirects ====
    324 
    325  * Execute the following SQL:
    326    {{{
    327    ALTER TABLE redirects RENAME TO django_redirects;
    328    INSERT INTO packages (label, name) VALUES ('redirects', 'redirects');
    329    UPDATE content_types SET package = 'redirects', python_module_name = 'redirects' WHERE package = 'core' AND python_module_name = 'redirects';
    330    UPDATE auth_permissions SET package = 'redirects' WHERE package = 'core' AND codename IN ('add_redirect', 'change_redirect', 'delete_redirect');
    331    }}}
    332  * If you're using PostgreSQL, execute this additional SQL:
    333    {{{
    334    ALTER TABLE redirects_id_seq RENAME TO django_redirects_id_seq;
    335    ALTER TABLE django_redirects ALTER COLUMN id DROP DEFAULT;
    336    ALTER TABLE django_redirects ALTER COLUMN id SET DEFAULT nextval('public.django_redirects_id_seq'::text);
    337    }}}
    338  * Add {{{"django.contrib.redirects"}}} to your {{{INSTALLED_APPS}}}.
    339  * Add {{{"django.contrib.redirects.middleware.RedirectFallbackMiddleware"}}} to your {{{MIDDLEWARE_CLASSES}}}.
    340 
    341 == Refactored RSS framework ==
    342 
    343 As of [1194], Django's RSS framework was refactored. This change should not affect most Django users, because the RSS framework was completely undocumented. The only users affected are people who reverse-engineered the framework, and WorldOnline.
    344 
    345 See the new [http://www.djangoproject.com/documentation/syndication/ syndication docs]. For completeness, here's what changed:
    346 
    347  * Created {{{django/contrib/syndication}}}.
    348  * Removed {{{django/conf/urls/rss.py}}}. The syndication system doesn't require its own URLconf anymore.
    349  * Moved {{{django/views/rss/rss.py}}} to {{{django/contrib/syndication/views.py}}} and refactored it so that {{{feed()}}} takes {{{url}}} and {{{feed_dict}}} instead of {{{slug}}} and {{{param}}}.
    350  * Renamed {{{DefaultRssFeed}}} to {{{DefaultFeed}}} in {{{django/utils/feedgenerator.py}}}.
    351  * RSS feeds are now specified as subclasses of {{{django.contrib.syndication.feeds.Feed}}} instead of {{{django.core.rss.FeedConfiguration}}}. Syntax is completely different.
    352  * RSS feeds are now registered in URLconfs rather than in "magic" settings modules whose names end with "_rss".
    353  * Templates for RSS titles and descriptions now live in a {{{feeds}}} directory, not an {{{rss}}} directory.
    354 
    355 == Changed field name and length for auth.User password_md5 field ==
    356 
    357 As of [1327], the {{{password_md5}}} field in the {{{auth.User}}} model, which is used for authentication in the Django admin, was renamed to {{{password}}}, and its length was changed from 32 to 128 to accomodate longer hashes and password metadata, such as which hash algorithm to use. This affects everybody who uses the Django authentication system -- including users of the Django admin.
    358 
    359 Execute the following SQL to restore auth functionality:
    360 
    361  * PostgreSQL:
    362 {{{
    363 BEGIN;
    364 ALTER TABLE auth_users ADD COLUMN password varchar(128);
    365 UPDATE auth_users SET password = password_md5;
    366 ALTER TABLE auth_users ALTER COLUMN password SET NOT NULL;
    367 ALTER TABLE auth_users DROP COLUMN password_md5;
    368 COMMIT;
    369 }}}
    370 
    371  * MySQL:
    372 {{{
    373 BEGIN;
    374 ALTER TABLE `auth_users` CHANGE `password_md5` `password` VARCHAR(128) NOT NULL;
    375 COMMIT;
    376 }}}
    377 
    378  * SQLite:
    379 {{{
    380 BEGIN;
    381 ALTER TABLE auth_users RENAME TO auth_users_old;
    382 CREATE TABLE auth_users (
    383     id integer NOT NULL PRIMARY KEY,
    384     username varchar(30) NOT NULL UNIQUE,
    385     first_name varchar(30) NOT NULL,
    386     last_name varchar(30) NOT NULL,
    387     email varchar(75) NOT NULL,
    388     password varchar(128) NOT NULL,
    389     is_staff bool NOT NULL,
    390     is_active bool NOT NULL,
    391     is_superuser bool NOT NULL,
    392     last_login datetime NOT NULL,
    393     date_joined datetime NOT NULL
    394 );
    395 INSERT INTO auth_users (
    396         id,
    397         username,
    398         first_name,
    399         last_name,
    400         email,
    401         password,
    402         is_staff,
    403         is_active,
    404         is_superuser,
    405         last_login,
    406         date_joined
    407 )
    408 SELECT * FROM auth_users_old;
    409 DROP TABLE auth_users_old;
    410 COMMIT;
    411 }}}
    412 
    413 == Template system changes ==
    414 
    415 As of [1443], we changed the way custom template tags and filters are registered. If you've written custom template tags or filters, you'll need to make a couple of changes:
    416 
    417 === Filter arguments ===
    418 
    419 For any filters that don't take an argument, remove the second argument to the filter function.
    420 
    421 Old:
    422 {{{
    423 def lower(param, _):
    424     return param.lower()
    425 }}}
    426 
    427 New:
    428 {{{
    429 def lower(param):
    430     return param.lower()
    431 }}}
    432 
    433 The system now introspects the function's arguments to find out whether a filter argument is required.
    434 
    435 === Change the way your tags/filters are registered ===
    436 
    437 Old way:
    438 {{{
    439 from django.core import template
    440 
    441 template.register_filter('lower', lower, False)
    442 template.register_tag('current_time', do_current_time)
    443 }}}
    444 
    445 New way:
    446 {{{
    447 from django.core import template
    448 
    449 register = template.Library()
    450 register.filter('lower', lower)
    451 register.tag('current_time', do_current_time)
    452 }}}
    453 
    454 === Change template decorator calls ===
    455 
    456 If you're using the undocumented template decorators (simple_tag and inclusion_tag), change your calls to be members of the library class.
    457 
    458 Old way:
    459 {{{
    460 simple_tag(func)
    461 inclusion_tag('path/to/template')(func)
    462 }}}
    463 
    464 New way:
    465 {{{
    466 register.simple_tag(func)
    467 register.inclusion_tag('path/to/template')(func)
    468 }}}
    469 
    470 === Change template tags that use filters ===
    471 
    472 Filters are compiled at compile time now.
    473 
    474 Old way:
    475 {{{
    476 class SomethingNode(Node):
    477     def __init__(self, filter_string):
    478         self.filter_string = filter_string
    479 
    480     def render(self, context):
    481         var = resolve_variable_with_filters(self.filter_string, context)
    482         return var
    483 
    484 def do_something(parser, token):
    485     bits = token.split()
    486     filter_string = bits[1]
    487     return SomethingNode(filter_string)
    488 
    489 register_tag('something', do_something)
    490 }}}
    491 
    492 New way:
    493 {{{
    494 class SomethingNode(Node):
    495     def __init__(self, filter_expr):
    496         self.filter_expr = filter_expr
    497 
    498     def render(self, context):
    499         var = self.filter_expr.resolve(context)
    500         return var
    501 
    502 def do_something(parser, token):
    503     bits = token.split()
    504     filter_string = bits[1]
    505     filter_expr = parser.compile_filter(filter_string)
    506     return SomethingNode(filter_expr)
    507 
    508 register.tag('something', do_something)
    509 }}}
    510 
    511 See [http://www.djangoproject.com/documentation/templates_python/#writing-custom-template-filters Writing custom template filters] and [http://www.djangoproject.com/documentation/templates_python/#writing-custom-template-tags Writing custom template tags] for full documentation.
    512 
    513 === Templates ===
    514 
    515 Templates that are included in another template using {{{{% include %}}}} or {{{{% ssi %}}}}, or extend a template using {{{{% extends %}}}}, must explicitly {{{{% load %}}}} all libraries they need for their tags and filters. Some templates may have worked in the past due to previous requests registering tags : this will no longer work.
    516 
    517 == Added support for non-named groups in URLconf regular expressions ==
    518 
    519 As of [1470], Django URLconfs support non-named groups in URLconf regular expressions. This means you no longer have to specify the name of each group.
    520 
    521 For example, this old syntax:
    522 {{{
    523 (r'^(?P<item_id>\d{1,3})/$', 'foo.item_detail'),
    524 }}}
    525 
    526 Can be shortened to this syntax:
    527 {{{
    528 (r'^(\d{1,3})/$', 'foo.item_detail'),
    529 }}}
    530 
    531 The algorithm it follows is: If there are any named groups, it will use those, ignoring all non-named groups. Otherwise, it will pass all non-named groups as positional arguments. In both cases, it will pass any {{{extra_kwargs}}} as keyword arguments.
    532 
    533 The old syntax (named groups) still works. Use that in cases where you can't (or don't want to) couple the order of URL parameters with the order of your view function arguments.
    534 
    535 There are two small backwards-incompatibilities about this change:
    536 
    537  * If you've written custom middleware that implements {{{process_view()}}}, you'll need to change it so it takes {{{view_args}}} and {{{view_kwargs}}} arguments instead of {{{param_dict}}}:
    538 
    539    {{{
    540    def process_view(self, request, view_func, view_args, view_kwargs)
    541    }}}
    542 
    543  * On the off chance you have legacy URLconfs that have NO captured items but DO use capturing parenthesis (which until now would have had no effect), you'll need to either change your view code to accept the captured values, or uncapture them.
    544 
    545 == Changed behavior of MultiValueDict.items() ==
    546 
    547 In [1504], the {{{MultiValueDict}}} (and {{{QueryDict}}}) {{{items()}}} method was changed to return the *last* member of each list rather than the full list.
    548 
    549 This probably doesn't affect anybody, but it's listed here for completeness.
    550 
    551 Old behavior:
    552 {{{
    553 >>> q = QueryDict('a=1&a=2')
    554 >>> q.items()
    555 [('a', ['1', 2'])]
    556 }}}
    557 
    558 New behavior:
    559 {{{
    560 >>> q = QueryDict('a=1&a=2')
    561 >>> q.items()
    562 [('a', '2')]
    563 }}}
    564 
    565 To emulate the old behavior, use the new {{{lists()}}} method:
    566 
    567 {{{
    568 >>> q = QueryDict('a=1&a=2')
    569 >>> q.lists()
    570 [('a', ['1', 2'])]
    571 }}}
    572 
    573 == Removed undocumented '_or' database API parameter ==
    574 
    575 As of [1508], the {{{_or}}} parameter no longer works. Use the new {{{complex}}} parameter. This likely doesn't affect anybody except World Online, because {{{_or}}} has been undocumented and vehemently discouraged on the mailing list.
    576 
    577 == Removed the magic ==
    578 
    579 As of [2809], the [wiki:RemovingTheMagic magic-removal branch] has been merged. There's a LONG list of backwards-incompatible changes, and they're all documented on the RemovingTheMagic wiki page.
    58020
    58121== Database constraint names changed ==
Back to Top