Changes between Version 160 and Version 161 of RemovingTheMagic


Ignore:
Timestamp:
12/10/2006 09:51:44 AM (9 years ago)
Author:
anonymous
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • RemovingTheMagic

    v160 v161  
    1 = Removing the magic =
    2 
    3 The "magic-removal" branch made several sweeping changes to the Django codebase, removing warts that Django had accumulated over the years. Most changes involved the database API and removing some of its unneeded magic, and other changes involved improving the framework's simplicity and usability.
    4 
    5 As of May 1, 2006, these changes have been integrated into the Django development version (Subversion's trunk), and were released officially in the following Django release, 0.95.
    6 
    7 This document explains all changes.
    8 
    9 Additional resource: the [wiki:MagicRemovalCheatSheet "magic-removal" cheat sheet], which can be used as a reference for conversion to the "magic-removal" branch. It contains links to this document organized by functional areas.
    10 
    11 [[TOC(inline, RemovingTheMagic)]]
    12 
    13 == How to get the branch ==
    14 
    15 If you're running Django 0.91 at the moment and want to start playing with the new, magic-removal version, just check out Django's development version using this command:
    16 
    17 {{{
    18 svn co http://code.djangoproject.com/svn/django/trunk/
    19 }}}
    20 
    21 If you have been working from a checkout of the magic-removal branch and want to switch your checkout over to the new trunk, cd to your magic-removal directory and then:
    22 
    23 {{{
    24 svn switch http://code.djangoproject.com/svn/django/trunk/
    25 }}}
    26 
    27 '''Note to Windows users:''' There have been confirmed reports of a 0.95 SVN checkout not correctly overwriting older versions. To be safe, remove your old trunk folder before checking out.
    28 
    29 === Using two versions of Django side-by-side ===
    30 
    31 Here's one way to use Django 0.91 and the magic-removal trunk versions on the same machine. This assumes a release such as 0.90 or 0.91 is installed:
    32 
    33 {{{
    34 # Get the development/trunk code somewhere on your filesystem. In this example, we use /home/python/django.
    35 $ cd /home/python/django
    36 $ svn co http://code.djangoproject.com/svn/django/trunk
    37 
    38 # This will have created a "trunk" directory.
    39 
    40 # Whenever you want to use trunk, set the environment variable {{{PYTHONPATH}}} to the directory containing trunk.
    41 export PYTHONPATH=/home/python/django/trunk
    42 }}}
    43 
    44 == Overview ==
    45 
    46 The biggest changes are:
    47 
    48  * The magic package {{{django.models}}} no longer exists. To use models, just import the model class from wherever it lives on the Python path. Similarly, the magic modules (such as {{{django.models.polls}}} in the tutorial) no longer exist; now, you interact directly with the model class.
    49  * All automatic pluralization is gone.
    50  * The database API has changed in several ways.
    51  * Various packages, such as the Django template system (previously in {{{django.core.template}}}), have been moved around to make importing less verbose and easier to remember.
    52 
    53 == Database changes you'll need to make ==
    54 
    55 To upgrade from a previous Django installation, you'll need to make some database changes. Obviously, this doesn't apply if you're starting from scratch.
    56 
    57 === Rename core database tables ===
    58 
    59 We've renamed a bunch of the core Django tables. Due to functionality differences between the various database backends, the SQL to perform the necessary tasks varies slightly between engines.
    60 
    61 To upgrade in SQLite, execute this SQL in your database (some steps are more involved because SQLite has only limited ALTER TABLE functionality. We have to instead create working tables, move the data, and then replace the old tables with the new ones:
    62 
    63 {{{
    64 ALTER TABLE auth_groups RENAME TO auth_group;
    65 ALTER TABLE auth_groups_permissions RENAME TO auth_group_permissions;
    66 ALTER TABLE auth_messages RENAME TO auth_message;
    67 ALTER TABLE auth_permissions RENAME TO auth_permission;
    68 ALTER TABLE auth_users RENAME TO auth_user;
    69 ALTER TABLE auth_users_groups RENAME TO auth_user_groups;
    70 ALTER TABLE auth_users_user_permissions RENAME TO auth_user_user_permissions;
    71 ALTER TABLE content_types RENAME TO django_content_type;
    72 ALTER TABLE core_sessions RENAME TO django_session;
    73 ALTER TABLE django_flatpages RENAME TO django_flatpage;
    74 ALTER TABLE django_flatpages_sites RENAME TO django_flatpage_sites;
    75 ALTER TABLE django_redirects RENAME TO django_redirect;
    76 ALTER TABLE sites RENAME TO django_site;
    77 
    78 CREATE TABLE "django_content_type_new"  (
    79 "id" integer NOT NULL PRIMARY KEY,
    80 "name" varchar(100) NOT NULL,
    81 "app_label" varchar(100) NOT NULL,
    82 "model" varchar(100) NOT NULL,
    83 UNIQUE ("app_label", "model")
    84 );
    85 
    86 INSERT INTO django_content_type_new
    87 (id,name,app_label,model)
    88 SELECT id,name,package,python_module_name
    89 FROM django_content_type;
    90 
    91 
    92 ALTER TABLE django_content_type rename to django_content_type_old;
    93 ALTER TABLE django_content_type_new rename to django_content_type;
    94 
    95 DROP TABLE django_content_type_old;
    96 
    97 DROP TABLE packages;
    98 
    99 
    100 ALTER TABLE auth_permission ADD COLUMN content_type_id INTEGER;
    101 }}}
    102 
    103 To upgrade in MySQL , execute this SQL in your database:
    104 
    105 {{{
    106 ALTER TABLE auth_groups RENAME TO auth_group;
    107 ALTER TABLE auth_groups_permissions RENAME TO auth_group_permissions;
    108 ALTER TABLE auth_messages RENAME TO auth_message;
    109 ALTER TABLE auth_permissions RENAME TO auth_permission;
    110 ALTER TABLE auth_users RENAME TO auth_user;
    111 ALTER TABLE auth_users_groups RENAME TO auth_user_groups;
    112 ALTER TABLE auth_users_user_permissions RENAME TO auth_user_user_permissions;
    113 ALTER TABLE content_types RENAME TO django_content_type;
    114 ALTER TABLE core_sessions RENAME TO django_session;
    115 ALTER TABLE django_flatpages RENAME TO django_flatpage;
    116 ALTER TABLE django_flatpages_sites RENAME TO django_flatpage_sites;
    117 ALTER TABLE django_redirects RENAME TO django_redirect;
    118 ALTER TABLE sites RENAME TO django_site;
    119 DROP TABLE packages;
    120 ALTER TABLE django_content_type CHANGE package app_label VARCHAR(100) NOT NULL,
    121   CHANGE python_module_name model VARCHAR(100) NOT NULL;
    122 ALTER TABLE auth_permission ADD COLUMN content_type_id INTEGER;
    123 }}}
    124 
    125 PostgreSQL is different, because you have to rename sequences, too. To upgrade in PostgreSQL, execute this SQL in your database:
    126 
    127 {{{
    128 BEGIN;
    129 
    130 ALTER TABLE auth_groups RENAME TO auth_group;
    131 ALTER TABLE auth_groups_id_seq RENAME TO auth_group_id_seq;
    132 ALTER TABLE auth_group ALTER COLUMN id DROP DEFAULT;
    133 ALTER TABLE auth_group ALTER COLUMN id SET DEFAULT nextval('public.auth_group_id_seq'::text);
    134 
    135 ALTER TABLE auth_groups_permissions RENAME TO auth_group_permissions;
    136 ALTER TABLE auth_groups_permissions_id_seq RENAME TO auth_group_permissions_id_seq;
    137 ALTER TABLE auth_group_permissions ALTER COLUMN id DROP DEFAULT;
    138 ALTER TABLE auth_group_permissions ALTER COLUMN id SET DEFAULT nextval('public.auth_group_permissions_id_seq'::text);
    139 
    140 ALTER TABLE auth_messages RENAME TO auth_message;
    141 ALTER TABLE auth_messages_id_seq RENAME TO auth_message_id_seq;
    142 ALTER TABLE auth_message ALTER COLUMN id DROP DEFAULT;
    143 ALTER TABLE auth_message ALTER COLUMN id SET DEFAULT nextval('public.auth_message_id_seq'::text);
    144 
    145 ALTER TABLE auth_permissions RENAME TO auth_permission;
    146 ALTER TABLE auth_permissions_id_seq RENAME TO auth_permission_id_seq;
    147 ALTER TABLE auth_permission ALTER COLUMN id DROP DEFAULT;
    148 ALTER TABLE auth_permission ALTER COLUMN id SET DEFAULT nextval('public.auth_permission_id_seq'::text);
    149 
    150 ALTER TABLE auth_users RENAME TO auth_user;
    151 ALTER TABLE auth_users_id_seq RENAME TO auth_user_id_seq;
    152 ALTER TABLE auth_user ALTER COLUMN id DROP DEFAULT;
    153 ALTER TABLE auth_user ALTER COLUMN id SET DEFAULT nextval('public.auth_user_id_seq'::text);
    154 
    155 ALTER TABLE auth_users_groups RENAME TO auth_user_groups;
    156 ALTER TABLE auth_users_groups_id_seq RENAME TO auth_user_groups_id_seq;
    157 ALTER TABLE auth_user_groups ALTER COLUMN id DROP DEFAULT;
    158 ALTER TABLE auth_user_groups ALTER COLUMN id SET DEFAULT nextval('public.auth_user_groups_id_seq'::text);
    159 
    160 ALTER TABLE auth_users_user_permissions RENAME TO auth_user_user_permissions;
    161 ALTER TABLE auth_users_user_permissions_id_seq RENAME TO auth_user_user_permissions_id_seq;
    162 ALTER TABLE auth_user_user_permissions ALTER COLUMN id DROP DEFAULT;
    163 ALTER TABLE auth_user_user_permissions ALTER COLUMN id SET DEFAULT nextval('public.auth_user_user_permissions_id_seq'::text);
    164 
    165 ALTER TABLE content_types RENAME TO django_content_type;
    166 ALTER TABLE content_types_id_seq RENAME TO django_content_type_id_seq;
    167 ALTER TABLE django_content_type ALTER COLUMN id DROP DEFAULT;
    168 ALTER TABLE django_content_type ALTER COLUMN id SET DEFAULT nextval('public.django_content_type_id_seq'::text);
    169 
    170 ALTER TABLE core_sessions RENAME TO django_session;
    171 
    172 ALTER TABLE django_flatpages RENAME TO django_flatpage;
    173 ALTER TABLE django_flatpages_id_seq RENAME TO django_flatpage_id_seq;
    174 ALTER TABLE django_flatpage ALTER COLUMN id DROP DEFAULT;
    175 ALTER TABLE django_flatpage ALTER COLUMN id SET DEFAULT nextval('public.django_flatpage_id_seq'::text);
    176 
    177 ALTER TABLE django_flatpages_sites RENAME TO django_flatpage_sites;
    178 ALTER TABLE django_flatpages_sites_id_seq RENAME TO django_flatpage_sites_id_seq;
    179 ALTER TABLE django_flatpage_sites ALTER COLUMN id DROP DEFAULT;
    180 ALTER TABLE django_flatpage_sites ALTER COLUMN id SET DEFAULT nextval('public.django_flatpage_sites_id_seq'::text);
    181 
    182 ALTER TABLE django_redirects RENAME TO django_redirect;
    183 ALTER TABLE django_redirects_id_seq RENAME TO django_redirect_id_seq;
    184 ALTER TABLE django_redirect ALTER COLUMN id DROP DEFAULT;
    185 ALTER TABLE django_redirect ALTER COLUMN id SET DEFAULT nextval('public.django_redirect_id_seq'::text);
    186 
    187 ALTER TABLE auth_permission DROP COLUMN package;
    188 
    189 -- First try this:
    190 ALTER TABLE django_content_type DROP CONSTRAINT "content_types_package_fkey";
    191 -- If that didn't work, do this:
    192 ALTER TABLE django_content_type DROP CONSTRAINT "$1";
    193 
    194 ALTER TABLE django_content_type ADD COLUMN app_label varchar(100);
    195 UPDATE django_content_type SET app_label=package;
    196 ALTER TABLE django_content_type ALTER COLUMN app_label SET NOT NULL;
    197 ALTER TABLE django_content_type DROP COLUMN package;
    198 
    199 DROP TABLE packages;
    200 
    201 ALTER TABLE sites RENAME TO django_site;
    202 ALTER TABLE sites_id_seq RENAME TO django_site_id_seq;
    203 ALTER TABLE django_site ALTER COLUMN id DROP DEFAULT;
    204 ALTER TABLE django_site ALTER COLUMN id SET DEFAULT nextval('public.django_site_id_seq'::text);
    205 
    206 ALTER TABLE django_content_type rename python_module_name to model;
    207 
    208 ALTER TABLE auth_permission ADD COLUMN content_type_id INTEGER references django_content_type(id);
    209 
    210 COMMIT;
    211 }}}
    212 
    213 === Changes to the SQL for the comments app ===
    214 
    215 The comments app, still undocumented, also requires some database changes.
    216 
    217 In PostgreSQL:
    218 {{{
    219 BEGIN;
    220 ALTER TABLE comments RENAME TO comments_comment;
    221 ALTER TABLE comments_id_seq RENAME TO comments_comment_id_seq;
    222 
    223 ALTER TABLE comments_karma RENAME TO comments_karmascore;
    224 ALTER TABLE comments_karma_id_seq RENAME TO comments_karmascore_id_seq;
    225 
    226 ALTER TABLE comments_free RENAME TO comments_freecomment;
    227 ALTER TABLE comments_free_id_seq RENAME TO comments_freecomment_id_seq;
    228 
    229 ALTER TABLE comments_moderator_deletions RENAME TO comments_moderatordeletion;
    230 ALTER TABLE comments_moderator_deletions_id_seq RENAME TO comments_moderatordeletion_id_seq;
    231 
    232 ALTER TABLE comments_user_flags RENAME TO comments_userflag;
    233 ALTER TABLE comments_user_flags_id_seq RENAME TO comments_userflag_id_seq;
    234 
    235 COMMIT;
    236 }}}
    237 
    238 In other databases:
    239 {{{
    240 ALTER TABLE comments RENAME TO comments_comment;
    241 ALTER TABLE comments_karma RENAME TO comments_karmascore;
    242 ALTER TABLE comments_free RENAME TO comments_freecomment;
    243 ALTER TABLE comments_moderator_deletions RENAME TO comments_moderatordeletion;
    244 ALTER TABLE comments_user_flags RENAME TO comments_userflag;
    245 }}}
    246 
    247 === Auth_permissions case changes ===
    248 
    249 Changeset [2745] changed the {{{verbose_name}}}s of two models in the {{{django.contrib.auth}}} app -- {{{Group}}} and {{{User}}} -- to be lowercased instead of uppercased ({{{"users"}}} instead of {{{"Users"}}}). This breaks the {{{syncdb}}} function of manage.py due to the disparity in existing installations of the magic-removal branch. To fix your {{{auth_permissions}}} table, execute these SQL statements:
    250 
    251 {{{
    252 BEGIN;
    253 UPDATE auth_permission SET name='Can add group' WHERE codename='add_group';
    254 UPDATE auth_permission SET name='Can change group' WHERE codename='change_group';
    255 UPDATE auth_permission SET name='Can delete group' WHERE codename='delete_group';
    256 UPDATE auth_permission SET name='Can add user' WHERE codename='add_user';
    257 UPDATE auth_permission SET name='Can change user' WHERE codename='change_user';
    258 UPDATE auth_permission SET name='Can delete user' WHERE codename='delete_user';
    259 
    260 UPDATE django_content_type SET name='group' WHERE model='groups';
    261 UPDATE django_content_type SET name='user' WHERE model='users';
    262 COMMIT;
    263 }}}
    264 
    265 === django_content_type table changes ===
    266 
    267 For every record in the {{{django_content_type}}} table (formerly {{{content_types}}}), rename the {{{model}}} field so that it's a singular version of the word. For example:
    268 
    269 {{{
    270 UPDATE django_content_type SET model='group' WHERE model='groups';
    271 UPDATE django_content_type SET model='user' WHERE model='users';
    272 UPDATE django_content_type SET model='flatpage' WHERE model='flatpagess';
    273 }}}
    274 
    275 === SQLite: Drop Auth_permission.Package column ===
    276 
    277 '''User 'Rajesh Dhawan' notes that, in SQLite, the Foreign Key reference on column {{{package}}} from the {{{auth_permissions}}} table still causes problems after following all the above database conversions. Executing the following conversion routine eliminates the {{{package}}} column (SQLite doesn't support dropping of columns):'''
    278 
    279 {{{
    280 CREATE TABLE "auth_permission_new" (
    281     "id" integer NOT NULL PRIMARY KEY,
    282     "name" varchar(50) NOT NULL,
    283     "content_type_id" integer,
    284     "codename" varchar(100) NOT NULL,
    285     UNIQUE ("content_type_id", "codename")
    286 );
    287 
    288 
    289 insert into auth_permission_new
    290 (id, name, codename)
    291 select id, name, codename
    292 from auth_permission;
    293 
    294 alter table auth_permission rename to auth_permission_old;
    295 alter table auth_permission_new rename to auth_permission;
    296 
    297 drop table auth_permission_old;
    298 }}}
    299 
    300 === Entries in your Content Types table will now be wrong ===
    301 
    302 If you've been using the comments system, your comments will be related back to the objects via the content types table. These won't work after your upgrade because of the pluralization discrepancy between the new and the old: you'll get failures in {{{ContentType.get_object_for_this_type}}} complaining that {{{None}}} doesn't have a {{{_default_manager}}} attribute, and when you fix the arguments to {{{comment_form}}} and {{{get_comment_list}}} arguments you'll get more breakage because the {{{django_content_type}}} table is now out of whack. If you can't be bothered writing your own SQL to fix this, try [http://django.pastebin.com/762068 fix_content_types.py]; it seems to work for me. - [mailto:garth@deadlybloodyserious.com Garth].
    303 
    304 === Database table-naming scheme has been changed ===
    305 
    306 Database table names formerly were created by joining the {{{app_label}}} and {{{module_name}}}. Example: {{{polls_polls}}}.
    307 
    308 Because there's no longer any concept of {{{module_name}}}, database table names are now formed by joining the {{{app_label}}} and model class name (lower case). Example: {{{polls_poll}}}.
    309 
    310 As always, this behavior can be overridden on a per-model basis by specifying the {{{db_table}}} attribute in {{{class Meta}}} in your model.
    311 
    312 To upgrade, you'll either have to explicitly set {{{db_table}}} in your models or rename your database tables to fit the new naming scheme Django expects. We'd recommend setting {{{db_table}}}, because it's easier.
    313 
    314 == Code changes you'll need to make ==
    315 
    316 === Model class and Field classes renamed/relocated ===
    317 
    318 Change your models to import from {{{django.db.models}}} instead of {{{django.core.meta}}}.
    319 
    320 {{{
    321 #!python
    322 from django.db import models
    323 
    324 class Person(models.Model):
    325     first_name = models.CharField(maxlength=30)
    326     last_name = models.CharField(maxlength=30)
    327 }}}
    328 
    329 === Interact directly with model classes, not with magic modules ===
    330 
    331 Import the model class directly from the module in which it was defined. No more {{{django.models.*}}} magic.
    332 
    333 {{{
    334 #!python
    335 from myproject.people.models import Person
    336 p = Person(first_name='John', last_name='Smith')
    337 p.save()
    338 }}}
    339 
    340 === Include template extension explicitly ===
    341 
    342 Now, whenever referencing templates by name, you now pass the template's *full name* -- including its extension -- instead of the file's name without an extension.
    343 
    344 Given a template {{{polls/poll_detail.html}}}...
    345 
    346 Old:
    347 {{{
    348 get_template('polls/poll_detail')
    349 select_template(['polls/poll_detail', 'polls/poll_detail2'])
    350 render_to_response('polls/poll_detail', {'poll': p})
    351 }}}
    352 
    353 New:
    354 
    355 {{{
    356 get_template('polls/poll_detail.html')
    357 select_template(['polls/poll_detail.html', 'polls/poll_detail2.html'])
    358 render_to_response('polls/poll_detail.html', {'poll': p})
    359 }}}
    360 
    361 This is changed in templates themselves, too -- notably in the {{{ {% extends %} }}} and {{{ {% include %} }}} template tags:
    362 
    363 Old:
    364 {{{
    365 {% extends "base" %}
    366 {% include "some_snippet" %}
    367 }}}
    368 
    369 New:
    370 {{{
    371 {% extends "base.html" %}
    372 {% include "some_snippet.html" %}
    373 }}}
    374 
    375 As a result of this change, the {{{TEMPLATE_FILE_EXTENSION}}} setting is gone, because there's no need for it.
    376 
    377 Clearly, this also means you can start putting whatever extension you want on templates. So if you'd like your templates to have a {{{.txt}}} extension, go for it. If you'd like to keep using {{{.html}}}, that's fine too. If you want to invent your own extension, or not even *use* an extension, be our guest.
    378 
    379 All the templates for the {{{django.contrib}}} apps -- most notably, the admin site -- still use {{{.html}}} extensions.
    380 
    381 Custom template tags created via {{{inclusion_tag()}}} should note the explicit template name (with the extension). For example, use {{{inclusion_tag('foo/bar.html')}}} instead of {{{inclusion_tag('foo/bar')}}}.
    382 
    383 If you're using custom {{{template_name}}} arguments to generic views, don't forget to change those calls in your URLconf, too.
    384 
    385 Finally, note the syndication framework, which looks for templates with the name of the slug of each feed, now requires an '.html' extension on those templates. For example, if your feed has a slug {{{"hello"}}}, the syndication framework will look for the templates {{{feeds/hello_title.html}}} and {{{feeds/hello_description.html}}}. This is backwards-compatible.
    386 
    387 === Namespace simplification ===
    388 
    389 {{{django.utils.httpwrappers}}} has moved to {{{django.http}}}.
    390 
    391 {{{django.core.exceptions.Http404}}} has moved to {{{django.http.Http404}}}.
    392 
    393 {{{django.core.template}}} has moved to {{{django.template}}}.
    394 
    395 {{{django.core.formfields}}} has moved to {{{django.forms}}}.
    396 
    397 {{{django.core.extensions}}} has moved to {{{django.shortcuts}}}.
    398 
    399 {{{django.core.extensions.DjangoContext}}} has been renamed to {{{RequestContext}}} and moved to {{{django.template.RequestContext}}}.
    400 
    401 You'll need to remove ".core." from your {{{TEMPLATE_LOADERS}}} settings values. Old:
    402 {{{
    403 #!python
    404 TEMPLATE_LOADERS = (
    405     'django.core.template.loaders.filesystem.load_template_source',
    406     'django.core.template.loaders.app_directories.load_template_source',
    407 #    'django.core.template.loaders.eggs.load_template_source',
    408 )
    409 }}}
    410 
    411 New:
    412 
    413 {{{
    414 #!python
    415 TEMPLATE_LOADERS = (
    416     'django.template.loaders.filesystem.load_template_source',
    417     'django.template.loaders.app_directories.load_template_source',
    418 #    'django.template.loaders.eggs.load_template_source',
    419 )
    420 }}}
    421 
    422 Custom template tag definitions need a similar change. Old:
    423 {{{
    424 #!python
    425 from django.core import template
    426 register = template.Library()
    427 }}}
    428 
    429 New:
    430 {{{
    431 #!python
    432 from django.template import Library
    433 register = Library()
    434 }}}
    435 
    436 The "auth" and "core" models have been split and moved to {{{django.contrib}}} as follows:
    437 
    438     * {{{django.models.auth}}} has moved to {{{django.contrib.auth.models}}}.
    439     * {{{django.models.core.sites}}} has moved to {{{django.contrib.sites.models}}}.
    440     * {{{django.models.core.contenttypes}}} has moved to {{{django.contrib.contenttypes.models}}}.
    441     * {{{django.models.core.packages}}} has moved to {{{django.contrib.contenttypes.models}}}. (Note that "packages" are going away before magic-removal is done.)
    442 
    443 Session middleware has moved from {{{django.middleware.sessions.SessionMiddleware}}} to {{{django.contrib.sessions.middleware.SessionMiddleware}}}. Make sure to update your {{{MIDDLEWARE_CLASSES}}} setting, if you're using sessions.
    444 
    445 If you get the following errors upon starting the webserver:
    446 {{{
    447 admin.logentry: 'user' has relation with model User, which has not been installed
    448 admin.logentry: 'content_type' has relation with model ContentType, which has not been installed
    449 }}}
    450 you need to add the apps {{{'django.contrib.contenttypes'}}} and {{{'django.contrib.auth'}}} to your {{{INSTALLED_APPS}}}.
    451 
    452 
    453 Also, the {{{Session}}} model has moved from django/models/core.py to django/contrib/sessions/models.py. If you're accessing the {{{Session}}} model for some reason, note that location change.
    454 
    455 === The 'packages' module is no more ===
    456 
    457 Packages no longer exist (they were redundant).  If you've done lookups against content-types or permissions you'll need to modify your code slightly:
    458 
    459 || Old                                                      || New                                                    ||
    460 || {{{contenttypes.get_list(package__label__exact='foo')}}} || {{{ContentType.objects.filter(package__exact='foo')}}} ||
    461 || {{{permissions.get_list(package__label__exact='foo')}}}  || {{{Permission.objects.filter(package__exact='foo')}}}  ||
    462    
    463 === Model location changed ===
    464 
    465 In previous Django versions, models lived in a {{{models/}}} subdirectory of your app package. Now, they should live in a file {{{models.py}}} directly within your app package.
    466 
    467 === Changes to model syntax ===
    468 
    469  * {{{class META}}} should now be {{{class Meta}}}. The latter is easier on the eyes.
    470 
    471  * The following are no longer valid parameters to {{{class Meta}}} and should be removed:
    472     * {{{module_name}}}
    473     * {{{admin}}} (See "Moved admin options to 'class Admin'" below.)
    474     * {{{exceptions}}} (Just put your exceptions in the module that contains the models and access them normally.)
    475     * {{{module_constants}}} (Just put your constants in the module that contains the models and access them normally.)
    476     * {{{where_constraints}}} (Just use a custom manager. See "Custom managers, and multiple managers" below.)
    477 
    478 === Moved admin options to 'class Admin' ===
    479 
    480 Instead of {{{admin=meta.Admin}}} in the {{{class META}}}, all admin options are in an inner {{{class Admin}}}.
    481 
    482 Old:
    483 {{{
    484 #!python
    485 class Person(meta.Model):
    486     first_name = meta.CharField(maxlength=30)
    487     last_name = meta.CharField(maxlength=30)
    488     class META:
    489         admin = meta.Admin(
    490             list_display = ('first_name', 'last_name')
    491         )
    492 }}}
    493 
    494 New:
    495 {{{
    496 #!python
    497 class Person(models.Model):
    498     first_name = models.CharField(maxlength=30)
    499     last_name = models.CharField(maxlength=30)
    500     class Admin:
    501         list_display = ('first_name', 'last_name')
    502 }}}
    503 
    504 If you're using the admin interface but aren't specifying any admin options, just put a {{{pass}}} in that inner class.
    505 
    506 Old:
    507 {{{
    508 #!python
    509 class Person(meta.Model):
    510     first_name = meta.CharField(maxlength=30)
    511     last_name = meta.CharField(maxlength=30)
    512     class META:
    513         admin = meta.Admin()
    514 }}}
    515 
    516 New:
    517 {{{
    518 #!python
    519 class Person(models.Model):
    520     first_name = models.CharField(maxlength=30)
    521     last_name = models.CharField(maxlength=30)
    522     class Admin:
    523         pass
    524 }}}
    525 
    526 === `__repr__()` replaced by `__str__()` ===
    527 
    528 You should use `__str__()` where `__repr__()` was formerly used, i.e., as a string representation of the model.
    529 
    530 `__repr__()` returns to its standard Python purpose of returning a string suitable for debugging purposes (e.g., `<ObjectName more_info>`); unless you want to customize the returned string, explicitly overriding `__repr__` in a model class is unnecessary.
    531 
    532 For example::
    533 
    534 {{{
    535 class BlogPost(models.Model):
    536 
    537     [...]
    538 
    539     def __repr__(self):
    540         return '''<BlogPost title="%s" comments_enabled=%s>''' % (self.title, self.comments_enabled)
    541 
    542     def __str__(self):
    543         return self.title
    544 }}}
    545 
    546 
    547 === Database connection relocated/renamed ===
    548 
    549 For any code that uses the raw database connection, use {{{django.db.connection}}} instead of {{{django.core.db.db}}}.
    550 
    551 Old:
    552 {{{
    553 #!python
    554 from django.core.db import db
    555 cursor = db.cursor()
    556 }}}
    557 
    558 New:
    559 {{{
    560 #!python
    561 from django.db import connection
    562 cursor = connection.cursor()
    563 
    564 
    565 }}}
    566 
    567 Backend-specific functions, if you should need them, are available at {{{django.db.backend}}}.
    568 
    569 Old:
    570 {{{
    571 #!python
    572 from django.core import db
    573 db.quote_name('foo')
    574 }}}
    575 
    576 New:
    577 {{{
    578 #!python
    579 from django.db import backend
    580 backend.quote_name('foo')
    581 }}}
    582 
    583 Also, the various backend functionality has been split into three separate modules for each backend -- {{{base.py}}}, {{{creation.py}}} and {{{introspection.py}}}. This is purely for performance and memory savings, so that basic, everyday Django usage doesn't have to load the introspective functionality into memory.
    584 
    585 === `datetime` and `db` modules must be imported explicitly ===
    586 
    587 Formerly, each model method magically had access to the {{{datetime}}} module and to the variable {{{db}}}, which represents the current database connection. Now, those have to be imported explicitly.
    588 
    589 Old:
    590 {{{
    591 #!python
    592     def some_method(self):
    593         print datetime.datetime.now()
    594         cursor = db.cursor()
    595         cursor.execute("UPDATE something;")
    596 }}}
    597 
    598 New:
    599 {{{
    600 #!python
    601 import datetime
    602 from django.db import connection
    603 
    604 # ...
    605 
    606     def some_method(self):
    607         print datetime.datetime.now()
    608         cursor = connection.cursor()
    609         cursor.execute("UPDATE something;")
    610 }}}
    611 
    612 === Descriptor fields ===
    613 
    614 All "table-level" functions -- ways of retrieving records tablewide rather than performing instance-specific tasks -- are now accessed via a model class's {{{objects}}} attribute. They aren't direct methods of a model instance object because we want to keep the "table-wide" and "row-specific" namespaces separate.
    615 
    616 A model class's {{{objects}}} attribute is an instance of {{{django.db.models.manager.Manager}}}. A manager has the following methods, all of which return a {{{QuerySet}}} instance.
    617 
    618     * {{{all()}}} -- Returns a {{{QuerySet}}} of all objects in the database. This is like the old {{{get_list()}}}. Takes no arguments.
    619     * {{{filter(**kwargs)}}} -- Returns a {{{QuerySet}}}, filtered by the given keyword arguments. Lookup arguments are in the same style as previously, e.g. {{{pubdate__year=2005}}}, except you can leave off {{{__exact}}} as a convenience. For example, {{{name='John'}}} and {{{name__exact='John'}}} are equivalent. Note that for lookups between applications you can't omit {{{__exact}}}.
    620     * {{{exclude(**kwargs)}}} is the same as {{{filter()}}}, but returns objects where the given arguments are not true.
    621     * {{{order_by(*fieldnames)}}} -- Returns a {{{QuerySet}}}
    622     * {{{count()}}} -- Returns the count of all objects in the database.
    623     * {{{dates(field_name, kind)}}} -- Like the old {{{get_FIELD_list()}}} for date fields. For example, old-school {{{get_pubdate_list('year')}}} is now {{{dates('pubdate', 'year')}}}.
    624     * {{{delete()}}} -- Deletes all objects.
    625     * {{{distinct()}}} -- Returns a {{{QuerySet}}} with DISTINCT set.
    626     * {{{extra(select=None, where=None, params=None, tables=None)}}} -- Sets the {{{select}}}, {{{where}}}, {{{params}}} and {{{tables}}} arguments, which are in the same format as before.
    627     * {{{get(**kwargs)}}} -- Like the old {{{get_object()}}}. Returns an object or raises {{{DoesNotExist}}} on error.
    628     * {{{in_bulk(id_list)}}} -- Like the old {{{get_in_bulk()}}}.
    629     * {{{iterator()}}} -- Returns a generator that iterators over results.
    630     * {{{select_related()}}} -- Returns a {{{QuerySet}}} with the "select related" option (which acts the same as before) set.
    631     * {{{values(*fieldnames)}}} -- Like the old {{{get_values()}}}.
    632 
    633 Each {{{QuerySet}}} has the following methods, which return a clone of the query set with the appropriate changes made:
    634 
    635     * {{{filter(**kwargs)}}}
    636     * {{{order_by(*fieldnames)}}}
    637     * {{{iterator()}}}
    638     * {{{count()}}}
    639     * {{{get(**kwargs)}}}
    640     * {{{delete()}}}
    641     * {{{filter(**kwargs)}}}
    642     * {{{select_related()}}}
    643     * {{{order_by(*fieldnames)}}}
    644     * {{{distinct()}}}
    645     * {{{extra(select=None, where=None, params=None, tables=None)}}}
    646 
    647 Here are some examples, which use the following models:
    648 
    649 {{{
    650 #!python
    651 class Reporter(models.Model):
    652     fname = models.CharField(maxlength=30)
    653     lname = models.CharField(maxlength=30)
    654 
    655 class Site(models.Model):
    656     name = models.CharField(maxlength=20)
    657 
    658 class Article(models.Model):
    659     headline = models.CharField(maxlength=50)
    660     reporter = models.ForeignKey(Reporter)
    661     pub_date = models.DateField()
    662 
    663 
    664     sites = models.ManyToManyField(Site)
    665 }}}
    666 
    667 || '''Old syntax'''                                       || '''New syntax'''                             ||
    668 || {{{reporters.get_list()}}}                             || {{{Reporter.objects.all()}}}                       ||
    669 || {{{reporters.get_list(fname__exact='John')}}}          || {{{Reporter.objects.filter(fname='John')}}}        ||
    670 || {{{reporters.get_list(order_by=('-lname', 'fname'))}}} || {{{Reporter.objects.order_by('-lname', 'fname')}}} ||
    671 || {{{reporters.get_list(fname__exact='John', order_by=('lname',))}}} || {{{Reporter.objects.filter(fname='John').order_by('lname')}}} ||
    672 || {{{reporters.get_object(pk=3)}}}                       || {{{Reporter.objects.get(pk=3)}}}                   ||
    673 || {{{reporters.get_object(complex=(Q(...)|Q(...)))}}}    || {{{Reporter.objects.get(Q(...)|Q(...))}}}
    674 ||                     
    675 || {{{reporters.get_object(fname__contains='John')}}}     || {{{Reporter.objects.get(fname__contains='John')}}} ||
    676 || {{{reporters.get_list(fname__ne='John')}}}             || {{{Reporter.objects.exclude(fname='John')}}} (note that {{{ne}}} is no longer a valid lookup type) ||
    677 || (not previously possible)             || {{{Reporter.objects.exclude(fname__contains='n')}}} ||
    678 || {{{reporters.get_list(distinct=True)}}}                || {{{Reporter.objects.distinct()}}} ||
    679 || {{{reporters.get_list(offset=10, limit=5)}}}           || {{{Reporter.objects.all()[10:15]}}}||
    680 || {{{reporters.get_values()}}}                           || {{{Reporter.objects.values()}}} ||
    681 || {{{reporters.get_in_bulk([1, 2])}}}                    || {{{Reporter.objects.in_bulk([1, 2])}}} ||
    682 || {{{reporters.get_in_bulk([1, 2], fname__exact='John')}}} || {{{Reporter.objects.filter(fname='John').in_bulk([1, 2])}}} ||
    683 || '''Date lookup'''                                        ||                                              ||
    684 || {{{articles.get_pub_date_list('year')}}}                 || {{{Article.objects.dates('pub_date', 'year')}}} ||
    685 || '''Latest-object lookup'''                               ||                                               ||
    686 || {{{articles.get_latest()}}} (required {{{get_latest_by}}} in model) || {{{Article.objects.latest()}}} (with {{{get_latest_by}}} in model) ||
    687 || (Not previously possible)                                     || {{{Article.objects.latest('pub_date')}}} # Latest by pub_date (overrides {{{get_latest_by}}} field in model) ||
    688 || '''Many-to-one related lookup'''                        ||                                              ||
    689 || {{{article_obj.reporter_id}}}                                 || {{{article_obj.reporter.id}}} ||
    690 || {{{article_obj.get_reporter()}}}                              || {{{article_obj.reporter}}}    ||
    691 || {{{reporter_obj.get_article_list()}}}                         || {{{reporter_obj.article_set.all()}}} ||
    692 || {{{reporter_obj.get_article_list(headline__exact='Hello')}}}  || {{{reporter_obj.article_set.filter(headline='Hello')}}} ||
    693 || {{{reporter_obj.get_article_count()}}}                        || {{{reporter_obj.article_set.count()}}} ||
    694 || {{{reporter_obj.add_article(headline='Foo')}}}                || {{{reporter_obj.article_set.create(headline='Foo')}}} ||
    695 || (Alternate syntax)                                      || {{{reporter_obj.article_set.add(article_obj)}}} ||
    696 || ("values" lookup, etc., not previously possible)        || {{{reporter_obj.article_set.values()}}} ||
    697 || '''Many-to-many related lookup'''                       ||                         ||
    698 || {{{article_obj.get_site_list()}}}                            || {{{article_obj.sites.all()}}} ||
    699 || {{{article_obj.set_sites([s1.id, s2.id])}}}                   || {{{article_obj.sites.clear(); article_obj.sites.add(s1); article_obj.sites.add(s2)}}} ||
    700 || {{{article_obj.set_sites([s1.id]) # deletion}}}               || {{{article_obj.sites.remove(s2)}}} ||
    701 || {{{site_obj.get_article_list()}}}                            || {{{site_obj.article_set.all()}}} ||
    702 
    703 Note that related-object lookup uses the default manager of the related object, which means the API for accessing related objects is completely consistent with the API for accessing objects via a manager.
    704 
    705 Also note that managers can't be accessed from instances:
    706 
    707 {{{
    708 #!python
    709 p = Person.objects.get(pk=1)
    710 p.objects.all() # Raises AttributeError
    711 }}}
    712 
    713 === Override default manager name ('objects') ===
    714 
    715 If a model already has an {{{objects}}} attribute, you'll need to specify an alternate name for the {{{objects}}} manager.
    716 
    717 {{{
    718 #!python
    719 class Person(models.Model):
    720     first_name = models.CharField(maxlength=30)
    721     last_name = models.CharField(maxlength=30)
    722     objects = models.TextField()
    723     people = models.Manager()
    724 
    725 p = Person(first_name='Mary', last_name='Jones', objects='Hello there.')
    726 p.save()
    727 p.objects == 'Hello there.'
    728 Person.people.all()
    729 }}}
    730 
    731 === Custom managers, and multiple managers ===
    732 
    733 You can create as many managers as you want. When necessary (such as on the admin), Django will use the first one defined, in order.
    734 
    735 If you define at least one custom manager, it will not get the default "objects" manager.
    736 
    737 {{{
    738 #!python
    739 class Person(models.Model):
    740     first_name = models.CharField(maxlength=30)
    741     last_name = models.CharField(maxlength=30)
    742     people = models.Manager()
    743     fun_people = SomeOtherManager()
    744 }}}
    745 
    746 If a manager needs to access its associated model class, it should use {{{self.model}}}. Example:
    747 
    748 {{{
    749 #!python
    750 class PersonManager(models.Manager):
    751     def get_fun_person(self):
    752         try:
    753             return self.get(fun=True)
    754         except self.model.DoesNotExist:
    755             print "Doesn't exist."
    756 }}}
    757 
    758 ==== Using a custom manager for the admin ====
    759 
    760 Sometimes you'll want to use a different manager for the admin displays (e.g. to display only objects matching some criteria in the admin).  You can do this by defining the {{{manager}}} option in your {{{Admin}}} declaration:
    761 
    762 {{{
    763 #!python
    764 
    765 
    766 class LivingPeopleManager(models.Manager):
    767     def get_query_set(self):
    768         return super(LivingPeopleManager, self).get_query_set().filter(is_alive=True)
    769 
    770 class Person(models.Model):
    771     name = models.CharField(maxlength=50)
    772     is_alive = models.BooleanField()
    773    
    774     class Admin:
    775         manager = LivingPeopleManager()
    776 
    777 }}}
    778 
    779 (see "You can override default QuerySets" for more on QuerySets)
    780 
    781 === Overriding save() and delete() model methods ===
    782 
    783 Proper subclassing of methods now works, so you can subclass the automatic {{{save()}}} and {{{delete()}}} methods. This removes the need for the {{{_pre_save()}}}, {{{_post_save()}}}, {{{_pre_delete()}}} and {{{_post_delete()}}} hooks -- all of which have been removed. Example:
    784 
    785 {{{
    786 #!python
    787 class Person(models.Model):
    788     first_name = models.CharField(maxlength=30)
    789     last_name = models.CharField(maxlength=30)
    790 
    791     def save(self):
    792         self.do_something()
    793         super(Person, self).save() # Call the "real" save() method.
    794         self.do_something_else()
    795 }}}
    796 
    797 You can even skip saving (as requested in #1014).
    798 
    799 {{{
    800 #!python
    801 class Person(models.Model):
    802     first_name = models.CharField(maxlength=30)
    803     last_name = models.CharField(maxlength=30)
    804 
    805     def save(self):
    806         if datetime.date.today() > datetime.date(2005, 1, 1):
    807             super(Person, self).save() # Call the "real" save() method.
    808         else:
    809             # Don't save.
    810             pass
    811 }}}
    812 
    813 === Renamed !DoesNotExist exception ===
    814 
    815 Instead of {{{people.PersonDoesNotExist}}}, it's {{{Person.DoesNotExist}}}.
    816 
    817 Old:
    818 {{{
    819 #!python
    820 from django.models.myapp import people
    821 try:
    822     people.get_object(pk=1)
    823 except people.PersonDoesNotExist:
    824     print "Not there"
    825 }}}
    826 
    827 New:
    828 {{{
    829 #!python
    830 from path.to.myapp.models import Person
    831 try:
    832     Person.objects.get(pk=1)
    833 except Person.DoesNotExist:
    834     print "Not there"
    835 }}}
    836 
    837 === Moved admin URLconf to shorten its path ===
    838 
    839 You'll need to change your URLconf to {{{include}}} the new location.
    840 
    841  * Old: {{{django.contrib.admin.urls.admin}}}
    842  * New: {{{django.contrib.admin.urls}}}
    843 
    844 === get_object_or_404 and get_list_or_404 take model classes ===
    845 
    846 Formerly, these helper methods took a model module as their first positional argument. Now, they expect a model class.
    847 
    848 Old:
    849 {{{
    850 #!python
    851 get_object_or_404(polls, pk=1)
    852 }}}
    853 
    854 New:
    855 {{{
    856 #!python
    857 get_object_or_404(Poll, pk=1)
    858 }}}
    859 
    860 
    861 === Changed the parameters you pass to generic views ===
    862 
    863 Because there's no longer a concept of {{{module_name}}}, the "info_dicts" passed to [http://www.djangoproject.com/documentation/generic_views/ generic views] no longer accept {{{"app_label"}}} and {{{"module_name"}}}. Instead, pass the parameter {{{"queryset"}}}, which should be a {{{QuerySet}}} instance.
    864 
    865 Old:
    866 {{{
    867 #!python
    868 info_dict = {
    869     'app_label': 'blog',
    870     'module_name': 'entries'
    871 }
    872 }}}
    873 
    874 New:
    875 {{{
    876 #!python
    877 from myproject.blog.models import Entry
    878 info_dict = {'queryset': Entry.objects.all()}
    879 }}}
    880 
    881 === Changed template names in generic views ===
    882 
    883 Because there's no longer a concept of {{{module_name}}}, [http://www.djangoproject.com/documentation/generic_views/ generic views] no longer create templates based on the {{{module_name}}}. Wherever they used {{{module_name}}}, they now use {{{model_name}}}, a lowercase version of the model name.
    884 
    885 Note that {{{app_label}}} remains the same.
    886 
    887 These examples assume models live in {{{myproject/blog/models.py}}}.
    888 
    889  * Old: {{{blog/entries_archive.html}}}
    890  * New: {{{blog/entry_archive.html}}}
    891 
    892 === Moved settings into an instance ===
    893 
    894 Settings have moved out of a dedicated module {{{django.conf.settings}}} into an instance in the {{{django.conf}}} module. So now you need to import the {{{settings}}} object and reference settings as attributes of that instance.
    895 
    896  * Old: {{{from django.conf.settings import LANGUAGE_CODE}}}
    897  * New: {{{from django.conf import settings}}}
    898 
    899 Wrappers around the Django machinery can make use of this by exchanging the settings instance with a proxy instance that delegates attribute access to a per-thread or per-location global.
    900 
    901 === Removed !SilentVariableFailure exception ===
    902 
    903 Old behavior: Any exception that subclasses {{{django.core.template.SilentVariableFailure}}} fails silently in the template system.
    904 
    905 New behavior: Any exception that has a {{{silent_variable_failure}}} attribute fails silently in the template system. {{{django.core.template.SilentVariableFailure}}} no longer exists.
    906 
    907 === request.user is now set via middleware ===
    908 
    909 It used to be set in the mod_python and wsgi handlers. You will need to add {{{"django.contrib.auth.middleware.AuthenticationMiddleware"}}} somewhere '''after''' {{{"django.contrib.sessions.middleware.SessionMiddleware"}}} in {{{MIDDLEWARE_CLASSES}}} in your settings.py file. Otherwise accessing {{{request.user}}} will raise an {{{AttributeError}}}.
    910 
    911 === Authentication has been consolidated ===
    912 
    913 Previously, pieces of the authentication system resided in at least 4 different places. Everything has now been consolidated into {{{django.contrib.auth}}} as follows:
    914 
    915  * {{{django.parts.auth.formfields.AuthenticationForm}}} has moved to {{{django.contrib.auth.forms}}}
    916  * {{{django.parts.auth.anonymoususers.AnonymousUser}}} has moved to {{{django.contrib.auth.models}}}
    917 
    918  * {{{django.views.auth.login.*}}} has moved to {{{django.contrib.auth.views}}}
    919  * {{{django.views.decorators.auth.*}}} has moved to {{{django.contrib.auth.decorators}}}
    920 
    921  * {{{django.views.registration.passwords.PasswordResetForm}}} has moved to {{{django.contrib.auth.forms}}}
    922  * {{{django.views.registration.passwords.PasswordChangeForm}}} has moved to {{{django.contrib.auth.forms}}}
    923 
    924  * {{{django.views.registration.passwords.password_reset}}} has moved to {{{django.contrib.auth.views}}}
    925  * {{{django.views.registration.passwords.password_reset_done}}} has moved to {{{django.contrib.auth.views}}}
    926  * {{{django.views.registration.passwords.password_change}}} has moved to {{{django.contrib.auth.views}}}
    927  * {{{django.views.registration.passwords.password_change_done}}} has moved to {{{django.contrib.auth.views}}}
    928 
    929 If you are using any of these classes or functions, you will need to update your code accordingly.
    930 
    931 === Changed interface to manipulators ===
    932 
    933 Old:
    934 {{{
    935 #!python
    936 from django.core import formfields
    937 from django.models.PROJECT import MODELMODULE
    938 ...
    939 manipulator = MODELMODULE.AddManipulator()
    940 ...
    941 form = formfields.FormWrapper(manipulator, new_data, errors)
    942 }}}
    943 
    944 New:
    945 {{{
    946 #!python
    947 from django.forms import FormWrapper
    948 
    949 from PROJECT.APP.models import MODELNAME
    950 ...
    951 manipulator = MODELNAME.AddManipulator()
    952 ...
    953 form = FormWrapper(manipulator, new_data, errors)
    954 }}}
    955 
    956 
    957 === Slightly changed django.VERSION ===
    958 
    959 The variable {{{django.VERSION}}} has changed from a tuple of four elements to a tuple of three elements.
    960 
    961 Old: {{{VERSION = (0, 9, 1, 'magic-removal')}}}
    962 
    963 New: {{{VERSION = (0, 95, 'post-magic-removal')}}}
    964 
    965 
    966 == New functionality you can start using ==
    967 
    968 
    969 === Models support properties ===
    970 
    971 Unlike before, properties are supported on models.
    972 
    973 {{{
    974 #!python
    975 from django.db import models
    976 
    977 class Person(models.Model):
    978     first_name = models.CharField(maxlength=30)
    979     last_name = models.CharField(maxlength=30)
    980 
    981     def _get_full_name(self):
    982         return "%s %s" % (self.first_name, self.last_name)
    983     full_name = property(_get_full_name)
    984 }}}
    985 
    986 === You can override default !QuerySets ===
    987 
    988 You can specify the default !QuerySet (see "Descriptor fields" above) that a manager uses. For example:
    989 
    990 {{{
    991 #!python
    992 class PublishedBookManager(models.Manager):
    993     def get_query_set(self):
    994         return super(PublishedBookManager, self).get_query_set().filter(is_published=True)
    995 
    996 class Book(models.Model):
    997     title = models.CharField(maxlength=50)
    998     author = models.CharField(maxlength=30)
    999     is_published = models.BooleanField()
    1000     published_objects = PublishedBookManager()
    1001 }}}
    1002 
    1003 == Documentation status ==
    1004 
    1005 The documentation on djangoproject.com has been updated to focus on trunk. [http://www.djangoproject.com/documentation/0_91/ Documentation for 0.91] has been archived.
    1006 
    1007 A few improvements still need to be made to the new documentation, however. Adrian is proofreading each document to make sure all is well. Here's the status.
    1008 
    1009 ||Document||Edited by Adrian||
    1010 ||add_ons.txt||Yes||
    1011 ||admin_css.txt||Yes||
    1012 ||apache_auth.txt||Yes||
    1013 ||authentication.txt||Yes||
    1014 ||cache.txt||Yes||
    1015 ||contributing.txt||Yes||
    1016 
    1017 ||db-api.txt||Yes||
    1018 ||design_philosophies.txt||Yes||
    1019 ||django-admin.txt||Yes||
    1020 ||email.txt||Yes||
    1021 ||faq.txt||Yes||
    1022 ||flatpages.txt||Yes||
    1023 ||forms.txt||Yes||
    1024 ||generic_views.txt||Yes||
    1025 ||i18n.txt||Yes||
    1026 ||install.txt||Yes||
    1027 ||legacy_databases.txt||Yes||
    1028 ||middleware.txt||Yes||
    1029 ||model-api.txt||Yes||
    1030 ||modpython.txt||Yes||
    1031 ||outputting_csv.txt||Yes||
    1032 ||outputting_pdf.txt||Yes||
    1033 ||overview.txt||Yes||
    1034 ||redirects.txt||Yes||
    1035 ||request_response.txt||Yes||
    1036 ||sessions.txt||Yes||
    1037 ||settings.txt||Yes||
    1038 ||static_files.txt||Yes||
    1039 ||syndication_feeds.txt||Yes||
    1040 ||templates_python.txt||Yes||
    1041 ||templates.txt||Yes||
    1042 ||transactions.txt||Yes||
    1043 ||tutorial01.txt||'''No'''||
    1044 ||tutorial02.txt||'''No'''||
    1045 ||tutorial03.txt||'''No'''||
    1046 ||tutorial04.txt||'''No'''||
    1047 ||url_dispatch.txt||Yes||
    1048 
    1049 The [http://www.djangoproject.com/documentation/models/ model examples], which are generated from Django's unit tests, also need to be updated. This is just a matter of updating the script that generates the examples from the tests.
     1[http://www.sj83.com/google.htm google排名]
     2[http://www.global-jipiao.cn/ 特价机票]
     3[http://www.beijingyizheng.com/zhongkongban/ 塑料周转箱]
     4[http://www.njjghg.com/user/jghg/index_cn.asp 偶联剂]
     5[http://www.bawwgt.com/ powerleveling]
Back to Top