summaryrefslogtreecommitdiff
path: root/parts/django/docs/ref/contrib/admin
diff options
context:
space:
mode:
authorNishanth Amuluru2011-01-08 11:20:57 +0530
committerNishanth Amuluru2011-01-08 11:20:57 +0530
commit65411d01d448ff0cd4abd14eee14cf60b5f8fc20 (patch)
treeb4c404363c4c63a61d6e2f8bd26c5b057c1fb09d /parts/django/docs/ref/contrib/admin
parent2e35094d43b4cc6974172e1febf76abb50f086ec (diff)
downloadpytask-65411d01d448ff0cd4abd14eee14cf60b5f8fc20.tar.gz
pytask-65411d01d448ff0cd4abd14eee14cf60b5f8fc20.tar.bz2
pytask-65411d01d448ff0cd4abd14eee14cf60b5f8fc20.zip
Added buildout stuff and made changes accordingly
--HG-- rename : profile/management/__init__.py => eggs/djangorecipe-0.20-py2.6.egg/EGG-INFO/dependency_links.txt rename : profile/management/__init__.py => eggs/djangorecipe-0.20-py2.6.egg/EGG-INFO/not-zip-safe rename : profile/management/__init__.py => eggs/infrae.subversion-1.4.5-py2.6.egg/EGG-INFO/dependency_links.txt rename : profile/management/__init__.py => eggs/infrae.subversion-1.4.5-py2.6.egg/EGG-INFO/not-zip-safe rename : profile/management/__init__.py => eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/EGG-INFO/dependency_links.txt rename : profile/management/__init__.py => eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/EGG-INFO/not-zip-safe rename : profile/management/__init__.py => eggs/py-1.4.0-py2.6.egg/EGG-INFO/dependency_links.txt rename : profile/management/__init__.py => eggs/py-1.4.0-py2.6.egg/EGG-INFO/not-zip-safe rename : profile/management/__init__.py => eggs/zc.buildout-1.5.2-py2.6.egg/EGG-INFO/dependency_links.txt rename : profile/management/__init__.py => eggs/zc.buildout-1.5.2-py2.6.egg/EGG-INFO/not-zip-safe rename : profile/management/__init__.py => eggs/zc.recipe.egg-1.3.2-py2.6.egg/EGG-INFO/dependency_links.txt rename : profile/management/__init__.py => eggs/zc.recipe.egg-1.3.2-py2.6.egg/EGG-INFO/not-zip-safe rename : profile/management/__init__.py => parts/django/Django.egg-info/dependency_links.txt rename : taskapp/models.py => parts/django/django/conf/app_template/models.py rename : taskapp/tests.py => parts/django/django/conf/app_template/tests.py rename : taskapp/views.py => parts/django/django/conf/app_template/views.py rename : taskapp/views.py => parts/django/django/contrib/gis/tests/geo3d/views.py rename : profile/management/__init__.py => parts/django/tests/modeltests/delete/__init__.py rename : profile/management/__init__.py => parts/django/tests/modeltests/files/__init__.py rename : profile/management/__init__.py => parts/django/tests/modeltests/invalid_models/__init__.py rename : profile/management/__init__.py => parts/django/tests/modeltests/m2m_signals/__init__.py rename : profile/management/__init__.py => parts/django/tests/modeltests/model_package/__init__.py rename : profile/management/__init__.py => parts/django/tests/regressiontests/bash_completion/__init__.py rename : profile/management/__init__.py => parts/django/tests/regressiontests/bash_completion/management/__init__.py rename : profile/management/__init__.py => parts/django/tests/regressiontests/bash_completion/management/commands/__init__.py rename : profile/management/__init__.py => parts/django/tests/regressiontests/bash_completion/models.py rename : profile/management/__init__.py => parts/django/tests/regressiontests/delete_regress/__init__.py rename : profile/management/__init__.py => parts/django/tests/regressiontests/file_storage/__init__.py rename : profile/management/__init__.py => parts/django/tests/regressiontests/max_lengths/__init__.py rename : profile/forms.py => pytask/profile/forms.py rename : profile/management/__init__.py => pytask/profile/management/__init__.py rename : profile/management/commands/seed_db.py => pytask/profile/management/commands/seed_db.py rename : profile/models.py => pytask/profile/models.py rename : profile/templatetags/user_tags.py => pytask/profile/templatetags/user_tags.py rename : taskapp/tests.py => pytask/profile/tests.py rename : profile/urls.py => pytask/profile/urls.py rename : profile/utils.py => pytask/profile/utils.py rename : profile/views.py => pytask/profile/views.py rename : static/css/base.css => pytask/static/css/base.css rename : taskapp/tests.py => pytask/taskapp/tests.py rename : taskapp/views.py => pytask/taskapp/views.py rename : templates/base.html => pytask/templates/base.html rename : templates/profile/browse_notifications.html => pytask/templates/profile/browse_notifications.html rename : templates/profile/edit.html => pytask/templates/profile/edit.html rename : templates/profile/view.html => pytask/templates/profile/view.html rename : templates/profile/view_notification.html => pytask/templates/profile/view_notification.html rename : templates/registration/activate.html => pytask/templates/registration/activate.html rename : templates/registration/activation_email.txt => pytask/templates/registration/activation_email.txt rename : templates/registration/activation_email_subject.txt => pytask/templates/registration/activation_email_subject.txt rename : templates/registration/logged_out.html => pytask/templates/registration/logged_out.html rename : templates/registration/login.html => pytask/templates/registration/login.html rename : templates/registration/logout.html => pytask/templates/registration/logout.html rename : templates/registration/password_change_done.html => pytask/templates/registration/password_change_done.html rename : templates/registration/password_change_form.html => pytask/templates/registration/password_change_form.html rename : templates/registration/password_reset_complete.html => pytask/templates/registration/password_reset_complete.html rename : templates/registration/password_reset_confirm.html => pytask/templates/registration/password_reset_confirm.html rename : templates/registration/password_reset_done.html => pytask/templates/registration/password_reset_done.html rename : templates/registration/password_reset_email.html => pytask/templates/registration/password_reset_email.html rename : templates/registration/password_reset_form.html => pytask/templates/registration/password_reset_form.html rename : templates/registration/registration_complete.html => pytask/templates/registration/registration_complete.html rename : templates/registration/registration_form.html => pytask/templates/registration/registration_form.html rename : utils.py => pytask/utils.py
Diffstat (limited to 'parts/django/docs/ref/contrib/admin')
-rw-r--r--parts/django/docs/ref/contrib/admin/_images/article_actions.pngbin0 -> 38545 bytes
-rw-r--r--parts/django/docs/ref/contrib/admin/_images/article_actions_message.pngbin0 -> 22098 bytes
-rw-r--r--parts/django/docs/ref/contrib/admin/_images/flatfiles_admin.pngbin0 -> 42243 bytes
-rw-r--r--parts/django/docs/ref/contrib/admin/_images/user_actions.pngbin0 -> 27047 bytes
-rw-r--r--parts/django/docs/ref/contrib/admin/_images/users_changelist.pngbin0 -> 59355 bytes
-rw-r--r--parts/django/docs/ref/contrib/admin/actions.txt351
-rw-r--r--parts/django/docs/ref/contrib/admin/admindocs.txt161
-rw-r--r--parts/django/docs/ref/contrib/admin/index.txt1613
8 files changed, 2125 insertions, 0 deletions
diff --git a/parts/django/docs/ref/contrib/admin/_images/article_actions.png b/parts/django/docs/ref/contrib/admin/_images/article_actions.png
new file mode 100644
index 0000000..78a78ae
--- /dev/null
+++ b/parts/django/docs/ref/contrib/admin/_images/article_actions.png
Binary files differ
diff --git a/parts/django/docs/ref/contrib/admin/_images/article_actions_message.png b/parts/django/docs/ref/contrib/admin/_images/article_actions_message.png
new file mode 100644
index 0000000..6ea9439
--- /dev/null
+++ b/parts/django/docs/ref/contrib/admin/_images/article_actions_message.png
Binary files differ
diff --git a/parts/django/docs/ref/contrib/admin/_images/flatfiles_admin.png b/parts/django/docs/ref/contrib/admin/_images/flatfiles_admin.png
new file mode 100644
index 0000000..391a629
--- /dev/null
+++ b/parts/django/docs/ref/contrib/admin/_images/flatfiles_admin.png
Binary files differ
diff --git a/parts/django/docs/ref/contrib/admin/_images/user_actions.png b/parts/django/docs/ref/contrib/admin/_images/user_actions.png
new file mode 100644
index 0000000..fdbe2ad
--- /dev/null
+++ b/parts/django/docs/ref/contrib/admin/_images/user_actions.png
Binary files differ
diff --git a/parts/django/docs/ref/contrib/admin/_images/users_changelist.png b/parts/django/docs/ref/contrib/admin/_images/users_changelist.png
new file mode 100644
index 0000000..d5f9c01
--- /dev/null
+++ b/parts/django/docs/ref/contrib/admin/_images/users_changelist.png
Binary files differ
diff --git a/parts/django/docs/ref/contrib/admin/actions.txt b/parts/django/docs/ref/contrib/admin/actions.txt
new file mode 100644
index 0000000..0fab59e
--- /dev/null
+++ b/parts/django/docs/ref/contrib/admin/actions.txt
@@ -0,0 +1,351 @@
+=============
+Admin actions
+=============
+
+.. versionadded:: 1.1
+
+.. currentmodule:: django.contrib.admin
+
+The basic workflow of Django's admin is, in a nutshell, "select an object,
+then change it." This works well for a majority of use cases. However, if you
+need to make the same change to many objects at once, this workflow can be
+quite tedious.
+
+In these cases, Django's admin lets you write and register "actions" -- simple
+functions that get called with a list of objects selected on the change list
+page.
+
+If you look at any change list in the admin, you'll see this feature in
+action; Django ships with a "delete selected objects" action available to all
+models. For example, here's the user module from Django's built-in
+:mod:`django.contrib.auth` app:
+
+.. image:: _images/user_actions.png
+
+.. warning::
+
+ The "delete selected objects" action uses :meth:`QuerySet.delete()
+ <django.db.models.QuerySet.delete>` for efficiency reasons, which has an
+ important caveat: your model's ``delete()`` method will not be called.
+
+ If you wish to override this behavior, simply write a custom action which
+ accomplishes deletion in your preferred manner -- for example, by calling
+ ``Model.delete()`` for each of the selected items.
+
+ For more background on bulk deletion, see the documentation on :ref:`object
+ deletion <topics-db-queries-delete>`.
+
+Read on to find out how to add your own actions to this list.
+
+Writing actions
+===============
+
+The easiest way to explain actions is by example, so let's dive in.
+
+A common use case for admin actions is the bulk updating of a model. Imagine a
+simple news application with an ``Article`` model::
+
+ from django.db import models
+
+ STATUS_CHOICES = (
+ ('d', 'Draft'),
+ ('p', 'Published'),
+ ('w', 'Withdrawn'),
+ )
+
+ class Article(models.Model):
+ title = models.CharField(max_length=100)
+ body = models.TextField()
+ status = models.CharField(max_length=1, choices=STATUS_CHOICES)
+
+ def __unicode__(self):
+ return self.title
+
+A common task we might perform with a model like this is to update an
+article's status from "draft" to "published". We could easily do this in the
+admin one article at a time, but if we wanted to bulk-publish a group of
+articles, it'd be tedious. So, let's write an action that lets us change an
+article's status to "published."
+
+Writing action functions
+------------------------
+
+First, we'll need to write a function that gets called when the action is
+trigged from the admin. Action functions are just regular functions that take
+three arguments:
+
+ * The current :class:`ModelAdmin`
+ * An :class:`~django.http.HttpRequest` representing the current request,
+ * A :class:`~django.db.models.QuerySet` containing the set of objects
+ selected by the user.
+
+Our publish-these-articles function won't need the :class:`ModelAdmin` or the
+request object, but we will use the queryset::
+
+ def make_published(modeladmin, request, queryset):
+ queryset.update(status='p')
+
+.. note::
+
+ For the best performance, we're using the queryset's :ref:`update method
+ <topics-db-queries-update>`. Other types of actions might need to deal
+ with each object individually; in these cases we'd just iterate over the
+ queryset::
+
+ for obj in queryset:
+ do_something_with(obj)
+
+That's actually all there is to writing an action! However, we'll take one
+more optional-but-useful step and give the action a "nice" title in the admin.
+By default, this action would appear in the action list as "Make published" --
+the function name, with underscores replaced by spaces. That's fine, but we
+can provide a better, more human-friendly name by giving the
+``make_published`` function a ``short_description`` attribute::
+
+ def make_published(modeladmin, request, queryset):
+ queryset.update(status='p')
+ make_published.short_description = "Mark selected stories as published"
+
+.. note::
+
+ This might look familiar; the admin's ``list_display`` option uses the
+ same technique to provide human-readable descriptions for callback
+ functions registered there, too.
+
+Adding actions to the :class:`ModelAdmin`
+-----------------------------------------
+
+Next, we'll need to inform our :class:`ModelAdmin` of the action. This works
+just like any other configuration option. So, the complete ``admin.py`` with
+the action and its registration would look like::
+
+ from django.contrib import admin
+ from myapp.models import Article
+
+ def make_published(modeladmin, request, queryset):
+ queryset.update(status='p')
+ make_published.short_description = "Mark selected stories as published"
+
+ class ArticleAdmin(admin.ModelAdmin):
+ list_display = ['title', 'status']
+ ordering = ['title']
+ actions = [make_published]
+
+ admin.site.register(Article, ArticleAdmin)
+
+That code will give us an admin change list that looks something like this:
+
+.. image:: _images/article_actions.png
+
+That's really all there is to it! If you're itching to write your own actions,
+you now know enough to get started. The rest of this document just covers more
+advanced techniques.
+
+Advanced action techniques
+==========================
+
+There's a couple of extra options and possibilities you can exploit for more
+advanced options.
+
+Actions as :class:`ModelAdmin` methods
+--------------------------------------
+
+The example above shows the ``make_published`` action defined as a simple
+function. That's perfectly fine, but it's not perfect from a code design point
+of view: since the action is tightly coupled to the ``Article`` object, it
+makes sense to hook the action to the ``ArticleAdmin`` object itself.
+
+That's easy enough to do::
+
+ class ArticleAdmin(admin.ModelAdmin):
+ ...
+
+ actions = ['make_published']
+
+ def make_published(self, request, queryset):
+ queryset.update(status='p')
+ make_published.short_description = "Mark selected stories as published"
+
+Notice first that we've moved ``make_published`` into a method and renamed the
+`modeladmin` parameter to `self`, and second that we've now put the string
+``'make_published'`` in ``actions`` instead of a direct function reference. This
+tells the :class:`ModelAdmin` to look up the action as a method.
+
+Defining actions as methods gives the action more straightforward, idiomatic
+access to the :class:`ModelAdmin` itself, allowing the action to call any of the
+methods provided by the admin.
+
+.. _custom-admin-action:
+
+For example, we can use ``self`` to flash a message to the user informing her
+that the action was successful::
+
+ class ArticleAdmin(admin.ModelAdmin):
+ ...
+
+ def make_published(self, request, queryset):
+ rows_updated = queryset.update(status='p')
+ if rows_updated == 1:
+ message_bit = "1 story was"
+ else:
+ message_bit = "%s stories were" % rows_updated
+ self.message_user(request, "%s successfully marked as published." % message_bit)
+
+This make the action match what the admin itself does after successfully
+performing an action:
+
+.. image:: _images/article_actions_message.png
+
+Actions that provide intermediate pages
+---------------------------------------
+
+By default, after an action is performed the user is simply redirected back
+to the original change list page. However, some actions, especially more
+complex ones, will need to return intermediate pages. For example, the
+built-in delete action asks for confirmation before deleting the selected
+objects.
+
+To provide an intermediary page, simply return an
+:class:`~django.http.HttpResponse` (or subclass) from your action. For
+example, you might write a simple export function that uses Django's
+:doc:`serialization functions </topics/serialization>` to dump some selected
+objects as JSON::
+
+ from django.http import HttpResponse
+ from django.core import serializers
+
+ def export_as_json(modeladmin, request, queryset):
+ response = HttpResponse(mimetype="text/javascript")
+ serializers.serialize("json", queryset, stream=response)
+ return response
+
+Generally, something like the above isn't considered a great idea. Most of the
+time, the best practice will be to return an
+:class:`~django.http.HttpResponseRedirect` and redirect the user to a view
+you've written, passing the list of selected objects in the GET query string.
+This allows you to provide complex interaction logic on the intermediary
+pages. For example, if you wanted to provide a more complete export function,
+you'd want to let the user choose a format, and possibly a list of fields to
+include in the export. The best thing to do would be to write a small action
+that simply redirects to your custom export view::
+
+ from django.contrib import admin
+ from django.contrib.contenttypes.models import ContentType
+ from django.http import HttpResponseRedirect
+
+ def export_selected_objects(modeladmin, request, queryset):
+ selected = request.POST.getlist(admin.ACTION_CHECKBOX_NAME)
+ ct = ContentType.objects.get_for_model(queryset.model)
+ return HttpResponseRedirect("/export/?ct=%s&ids=%s" % (ct.pk, ",".join(selected)))
+
+As you can see, the action is the simple part; all the complex logic would
+belong in your export view. This would need to deal with objects of any type,
+hence the business with the ``ContentType``.
+
+Writing this view is left as an exercise to the reader.
+
+.. _adminsite-actions:
+
+Making actions available site-wide
+----------------------------------
+
+.. method:: AdminSite.add_action(action[, name])
+
+ Some actions are best if they're made available to *any* object in the admin
+ site -- the export action defined above would be a good candidate. You can
+ make an action globally available using :meth:`AdminSite.add_action()`. For
+ example::
+
+ from django.contrib import admin
+
+ admin.site.add_action(export_selected_objects)
+
+ This makes the `export_selected_objects` action globally available as an
+ action named `"export_selected_objects"`. You can explicitly give the action
+ a name -- good if you later want to programatically :ref:`remove the action
+ <disabling-admin-actions>` -- by passing a second argument to
+ :meth:`AdminSite.add_action()`::
+
+ admin.site.add_action(export_selected_objects, 'export_selected')
+
+.. _disabling-admin-actions:
+
+Disabling actions
+-----------------
+
+Sometimes you need to disable certain actions -- especially those
+:ref:`registered site-wide <adminsite-actions>` -- for particular objects.
+There's a few ways you can disable actions:
+
+Disabling a site-wide action
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. method:: AdminSite.disable_action(name)
+
+ If you need to disable a :ref:`site-wide action <adminsite-actions>` you can
+ call :meth:`AdminSite.disable_action()`.
+
+ For example, you can use this method to remove the built-in "delete selected
+ objects" action::
+
+ admin.site.disable_action('delete_selected')
+
+ Once you've done the above, that action will no longer be available
+ site-wide.
+
+ If, however, you need to re-enable a globally-disabled action for one
+ particular model, simply list it explicitly in your ``ModelAdmin.actions``
+ list::
+
+ # Globally disable delete selected
+ admin.site.disable_action('delete_selected')
+
+ # This ModelAdmin will not have delete_selected available
+ class SomeModelAdmin(admin.ModelAdmin):
+ actions = ['some_other_action']
+ ...
+
+ # This one will
+ class AnotherModelAdmin(admin.ModelAdmin):
+ actions = ['delete_selected', 'a_third_action']
+ ...
+
+
+Disabling all actions for a particular :class:`ModelAdmin`
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you want *no* bulk actions available for a given :class:`ModelAdmin`, simply
+set :attr:`ModelAdmin.actions` to ``None``::
+
+ class MyModelAdmin(admin.ModelAdmin):
+ actions = None
+
+This tells the :class:`ModelAdmin` to not display or allow any actions,
+including any :ref:`site-wide actions <adminsite-actions>`.
+
+Conditionally enabling or disabling actions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. method:: ModelAdmin.get_actions(request)
+
+ Finally, you can conditionally enable or disable actions on a per-request
+ (and hence per-user basis) by overriding :meth:`ModelAdmin.get_actions`.
+
+ This returns a dictionary of actions allowed. The keys are action names, and
+ the values are ``(function, name, short_description)`` tuples.
+
+ Most of the time you'll use this method to conditionally remove actions from
+ the list gathered by the superclass. For example, if I only wanted users
+ whose names begin with 'J' to be able to delete objects in bulk, I could do
+ the following::
+
+ class MyModelAdmin(admin.ModelAdmin):
+ ...
+
+ def get_actions(self, request):
+ actions = super(MyModelAdmin, self).get_actions(request)
+ if request.user.username[0].upper() != 'J':
+ del actions['delete_selected']
+ return actions
+
+
diff --git a/parts/django/docs/ref/contrib/admin/admindocs.txt b/parts/django/docs/ref/contrib/admin/admindocs.txt
new file mode 100644
index 0000000..6743921
--- /dev/null
+++ b/parts/django/docs/ref/contrib/admin/admindocs.txt
@@ -0,0 +1,161 @@
+========================================
+The Django admin documentation generator
+========================================
+
+.. module:: django.contrib.admindocs
+ :synopsis: Django's admin documentation generator.
+
+.. currentmodule:: django.contrib.admindocs
+
+Django's :mod:`~django.contrib.admindocs` app pulls documentation from the
+docstrings of models, views, template tags, and template filters for any app in
+:setting:`INSTALLED_APPS` and makes that documentation available from the
+:mod:`Django admin <django.contrib.admin>`.
+
+In addition to providing offline documentation for all template tags and
+template filters that ship with Django, you may utilize admindocs to quickly
+document your own code.
+
+Overview
+========
+
+To activate the :mod:`~django.contrib.admindocs`, you will need to do
+the following:
+
+ * Add :mod:`django.contrib.admindocs` to your :setting:`INSTALLED_APPS`.
+ * Add ``(r'^admin/doc/', include('django.contrib.admindocs.urls'))`` to
+ your :data:`urlpatterns`. Make sure it's included *before* the
+ ``r'^admin/'`` entry, so that requests to ``/admin/doc/`` don't get
+ handled by the latter entry.
+ * Install the docutils Python module (http://docutils.sf.net/).
+ * **Optional:** Linking to templates requires the :setting:`ADMIN_FOR`
+ setting to be configured.
+ * **Optional:** Using the admindocs bookmarklets requires the
+ :mod:`XViewMiddleware<django.middleware.doc>` to be installed.
+
+Once those steps are complete, you can start browsing the documentation by
+going to your admin interface and clicking the "Documentation" link in the
+upper right of the page.
+
+Documentation helpers
+=====================
+
+The following special markup can be used in your docstrings to easily create
+hyperlinks to other components:
+
+================= =======================
+Django Component reStructuredText roles
+================= =======================
+Models ``:model:`appname.ModelName```
+Views ``:view:`appname.view_name```
+Template tags ``:tag:`tagname```
+Template filters ``:filter:`filtername```
+Templates ``:template:`path/to/template.html```
+================= =======================
+
+Model reference
+===============
+
+The **models** section of the ``admindocs`` page describes each model in the
+system along with all the fields and methods available on it. Relationships to
+other models appear as hyperlinks. Descriptions are pulled from ``help_text``
+attributes on fields or from docstrings on model methods.
+
+A model with useful documentation might look like this::
+
+ class BlogEntry(models.Model):
+ """
+ Stores a single blog entry, related to :model:`blog.Blog` and
+ :model:`auth.User`.
+
+ """
+ slug = models.SlugField(help_text="A short label, generally used in URLs.")
+ author = models.ForeignKey(User)
+ blog = models.ForeignKey(Blog)
+ ...
+
+ def publish(self):
+ """Makes the blog entry live on the site."""
+ ...
+
+View reference
+==============
+
+Each URL in your site has a separate entry in the ``admindocs`` page, and
+clicking on a given URL will show you the corresponding view. Helpful things
+you can document in your view function docstrings include:
+
+ * A short description of what the view does.
+ * The **context**, or a list of variables available in the view's template.
+ * The name of the template or templates that are used for that view.
+
+For example::
+
+ from myapp.models import MyModel
+
+ def my_view(request, slug):
+ """
+ Display an individual :model:`myapp.MyModel`.
+
+ **Context**
+
+ ``RequestContext``
+
+ ``mymodel``
+ An instance of :model:`myapp.MyModel`.
+
+ **Template:**
+
+ :template:`myapp/my_template.html`
+
+ """
+ return render_to_response('myapp/my_template.html', {
+ 'mymodel': MyModel.objects.get(slug=slug)
+ }, context_instance=RequestContext(request))
+
+
+Template tags and filters reference
+===================================
+
+The **tags** and **filters** ``admindocs`` sections describe all the tags and
+filters that come with Django (in fact, the :ref:`built-in tag reference
+<ref-templates-builtins-tags>` and :ref:`built-in filter reference
+<ref-templates-builtins-filters>` documentation come directly from those
+pages). Any tags or filters that you create or are added by a third-party app
+will show up in these sections as well.
+
+
+Template reference
+==================
+
+While ``admindocs`` does not include a place to document templates by
+themselves, if you use the ``:template:`path/to/template.html``` syntax in a
+docstring the resulting page will verify the path of that template with
+Django's :ref:`template loaders <template-loaders>`. This can be a handy way to
+check if the specified template exists and to show where on the filesystem that
+template is stored.
+
+
+Included Bookmarklets
+=====================
+
+Several useful bookmarklets are available from the ``admindocs`` page:
+
+ Documentation for this page
+ Jumps you from any page to the documentation for the view that generates
+ that page.
+
+ Show object ID
+ Shows the content-type and unique ID for pages that represent a single
+ object.
+
+ Edit this object
+ Jumps to the admin page for pages that represent a single object.
+
+Using these bookmarklets requires that you are either logged into the
+:mod:`Django admin <django.contrib.admin>` as a
+:class:`~django.contrib.auth.models.User` with
+:attr:`~django.contrib.auth.models.User.is_staff` set to `True`, or
+that the :mod:`django.middleware.doc` middleware and
+:mod:`XViewMiddleware <django.middleware.doc>` are installed and you
+are accessing the site from an IP address listed in :setting:`INTERNAL_IPS`.
diff --git a/parts/django/docs/ref/contrib/admin/index.txt b/parts/django/docs/ref/contrib/admin/index.txt
new file mode 100644
index 0000000..b99cfdc
--- /dev/null
+++ b/parts/django/docs/ref/contrib/admin/index.txt
@@ -0,0 +1,1613 @@
+=====================
+The Django admin site
+=====================
+
+.. module:: django.contrib.admin
+ :synopsis: Django's admin site.
+
+One of the most powerful parts of Django is the automatic admin interface. It
+reads metadata in your model to provide a powerful and production-ready
+interface that content producers can immediately use to start adding content to
+the site. In this document, we discuss how to activate, use and customize
+Django's admin interface.
+
+.. admonition:: Note
+
+ The admin site has been refactored significantly since Django 0.96. This
+ document describes the newest version of the admin site, which allows for
+ much richer customization. If you follow the development of Django itself,
+ you may have heard this described as "newforms-admin."
+
+Overview
+========
+
+There are six steps in activating the Django admin site:
+
+ 1. Add ``'django.contrib.admin'`` to your :setting:`INSTALLED_APPS`
+ setting.
+
+ 2. Admin has two dependencies - ``django.contrib.auth`` and
+ ``django.contrib.contenttypes``. If these applications are not
+ in your :setting:`INSTALLED_APPS` list, add them.
+
+ 3. Determine which of your application's models should be editable in the
+ admin interface.
+
+ 4. For each of those models, optionally create a ``ModelAdmin`` class that
+ encapsulates the customized admin functionality and options for that
+ particular model.
+
+ 5. Instantiate an ``AdminSite`` and tell it about each of your models and
+ ``ModelAdmin`` classes.
+
+ 6. Hook the ``AdminSite`` instance into your URLconf.
+
+Other topics
+------------
+
+.. toctree::
+ :maxdepth: 1
+
+ actions
+ admindocs
+
+.. seealso::
+
+ For information about serving the media files (images, JavaScript, and CSS)
+ associated with the admin in production, see :ref:`serving-media-files`.
+
+``ModelAdmin`` objects
+======================
+
+.. class:: ModelAdmin
+
+The ``ModelAdmin`` class is the representation of a model in the admin
+interface. These are stored in a file named ``admin.py`` in your application.
+Let's take a look at a very simple example of the ``ModelAdmin``::
+
+ from django.contrib import admin
+ from myproject.myapp.models import Author
+
+ class AuthorAdmin(admin.ModelAdmin):
+ pass
+ admin.site.register(Author, AuthorAdmin)
+
+.. admonition:: Do you need a ``ModelAdmin`` object at all?
+
+ In the preceding example, the ``ModelAdmin`` class doesn't define any
+ custom values (yet). As a result, the default admin interface will be
+ provided. If you are happy with the default admin interface, you don't
+ need to define a ``ModelAdmin`` object at all -- you can register the
+ model class without providing a ``ModelAdmin`` description. The
+ preceding example could be simplified to::
+
+ from django.contrib import admin
+ from myproject.myapp.models import Author
+
+ admin.site.register(Author)
+
+``ModelAdmin`` Options
+----------------------
+
+The ``ModelAdmin`` is very flexible. It has several options for dealing with
+customizing the interface. All options are defined on the ``ModelAdmin``
+subclass::
+
+ class AuthorAdmin(admin.ModelAdmin):
+ date_hierarchy = 'pub_date'
+
+.. attribute:: ModelAdmin.date_hierarchy
+
+Set ``date_hierarchy`` to the name of a ``DateField`` or ``DateTimeField`` in
+your model, and the change list page will include a date-based drilldown
+navigation by that field.
+
+Example::
+
+ date_hierarchy = 'pub_date'
+
+.. attribute:: ModelAdmin.form
+
+By default a ``ModelForm`` is dynamically created for your model. It is used
+to create the form presented on both the add/change pages. You can easily
+provide your own ``ModelForm`` to override any default form behavior on the
+add/change pages.
+
+For an example see the section `Adding custom validation to the admin`_.
+
+.. attribute:: ModelAdmin.fieldsets
+
+Set ``fieldsets`` to control the layout of admin "add" and "change" pages.
+
+``fieldsets`` is a list of two-tuples, in which each two-tuple represents a
+``<fieldset>`` on the admin form page. (A ``<fieldset>`` is a "section" of the
+form.)
+
+The two-tuples are in the format ``(name, field_options)``, where ``name`` is a
+string representing the title of the fieldset and ``field_options`` is a
+dictionary of information about the fieldset, including a list of fields to be
+displayed in it.
+
+A full example, taken from the ``django.contrib.flatpages.FlatPage`` model::
+
+ class FlatPageAdmin(admin.ModelAdmin):
+ fieldsets = (
+ (None, {
+ 'fields': ('url', 'title', 'content', 'sites')
+ }),
+ ('Advanced options', {
+ 'classes': ('collapse',),
+ 'fields': ('enable_comments', 'registration_required', 'template_name')
+ }),
+ )
+
+This results in an admin page that looks like:
+
+ .. image:: _images/flatfiles_admin.png
+
+If ``fieldsets`` isn't given, Django will default to displaying each field
+that isn't an ``AutoField`` and has ``editable=True``, in a single fieldset,
+in the same order as the fields are defined in the model.
+
+The ``field_options`` dictionary can have the following keys:
+
+ * ``fields``
+ A tuple of field names to display in this fieldset. This key is
+ required.
+
+ Example::
+
+ {
+ 'fields': ('first_name', 'last_name', 'address', 'city', 'state'),
+ }
+
+ To display multiple fields on the same line, wrap those fields in
+ their own tuple. In this example, the ``first_name`` and ``last_name``
+ fields will display on the same line::
+
+ {
+ 'fields': (('first_name', 'last_name'), 'address', 'city', 'state'),
+ }
+
+ .. versionadded:: 1.2
+
+ ``fields`` can contain values defined in
+ :attr:`ModelAdmin.readonly_fields` to be displayed as read-only.
+
+ * ``classes``
+ A list containing extra CSS classes to apply to the fieldset.
+
+ Example::
+
+ {
+ 'classes': ['wide', 'extrapretty'],
+ }
+
+ Two useful classes defined by the default admin site stylesheet are
+ ``collapse`` and ``wide``. Fieldsets with the ``collapse`` style will
+ be initially collapsed in the admin and replaced with a small
+ "click to expand" link. Fieldsets with the ``wide`` style will be
+ given extra horizontal space.
+
+ * ``description``
+ A string of optional extra text to be displayed at the top of each
+ fieldset, under the heading of the fieldset.
+
+ Note that this value is *not* HTML-escaped when it's displayed in
+ the admin interface. This lets you include HTML if you so desire.
+ Alternatively you can use plain text and
+ ``django.utils.html.escape()`` to escape any HTML special
+ characters.
+
+.. attribute:: ModelAdmin.fields
+
+Use this option as an alternative to ``fieldsets`` if the layout does not
+matter and if you want to only show a subset of the available fields in the
+form. For example, you could define a simpler version of the admin form for
+the ``django.contrib.flatpages.FlatPage`` model as follows::
+
+ class FlatPageAdmin(admin.ModelAdmin):
+ fields = ('url', 'title', 'content')
+
+In the above example, only the fields 'url', 'title' and 'content' will be
+displayed, sequentially, in the form.
+
+.. versionadded:: 1.2
+
+``fields`` can contain values defined in :attr:`ModelAdmin.readonly_fields`
+to be displayed as read-only.
+
+.. admonition:: Note
+
+ This ``fields`` option should not be confused with the ``fields``
+ dictionary key that is within the ``fieldsets`` option, as described in
+ the previous section.
+
+.. attribute:: ModelAdmin.exclude
+
+This attribute, if given, should be a list of field names to exclude from the
+form.
+
+For example, let's consider the following model::
+
+ class Author(models.Model):
+ name = models.CharField(max_length=100)
+ title = models.CharField(max_length=3)
+ birth_date = models.DateField(blank=True, null=True)
+
+If you want a form for the ``Author`` model that includes only the ``name``
+and ``title`` fields, you would specify ``fields`` or ``exclude`` like this::
+
+ class AuthorAdmin(admin.ModelAdmin):
+ fields = ('name', 'title')
+
+ class AuthorAdmin(admin.ModelAdmin):
+ exclude = ('birth_date',)
+
+Since the Author model only has three fields, ``name``, ``title``, and
+``birth_date``, the forms resulting from the above declarations will contain
+exactly the same fields.
+
+.. attribute:: ModelAdmin.filter_horizontal
+
+Use a nifty unobtrusive JavaScript "filter" interface instead of the
+usability-challenged ``<select multiple>`` in the admin form. The value is a
+list of fields that should be displayed as a horizontal filter interface. See
+``filter_vertical`` to use a vertical interface.
+
+.. attribute:: ModelAdmin.filter_vertical
+
+Same as ``filter_horizontal``, but is a vertical display of the filter
+interface.
+
+.. attribute:: ModelAdmin.list_display
+
+Set ``list_display`` to control which fields are displayed on the change list
+page of the admin.
+
+Example::
+
+ list_display = ('first_name', 'last_name')
+
+If you don't set ``list_display``, the admin site will display a single column
+that displays the ``__unicode__()`` representation of each object.
+
+You have four possible values that can be used in ``list_display``:
+
+ * A field of the model. For example::
+
+ class PersonAdmin(admin.ModelAdmin):
+ list_display = ('first_name', 'last_name')
+
+ * A callable that accepts one parameter for the model instance. For
+ example::
+
+ def upper_case_name(obj):
+ return ("%s %s" % (obj.first_name, obj.last_name)).upper()
+ upper_case_name.short_description = 'Name'
+
+ class PersonAdmin(admin.ModelAdmin):
+ list_display = (upper_case_name,)
+
+ * A string representing an attribute on the ``ModelAdmin``. This behaves
+ same as the callable. For example::
+
+ class PersonAdmin(admin.ModelAdmin):
+ list_display = ('upper_case_name',)
+
+ def upper_case_name(self, obj):
+ return ("%s %s" % (obj.first_name, obj.last_name)).upper()
+ upper_case_name.short_description = 'Name'
+
+ * A string representing an attribute on the model. This behaves almost
+ the same as the callable, but ``self`` in this context is the model
+ instance. Here's a full model example::
+
+ class Person(models.Model):
+ name = models.CharField(max_length=50)
+ birthday = models.DateField()
+
+ def decade_born_in(self):
+ return self.birthday.strftime('%Y')[:3] + "0's"
+ decade_born_in.short_description = 'Birth decade'
+
+ class PersonAdmin(admin.ModelAdmin):
+ list_display = ('name', 'decade_born_in')
+
+A few special cases to note about ``list_display``:
+
+ * If the field is a ``ForeignKey``, Django will display the
+ ``__unicode__()`` of the related object.
+
+ * ``ManyToManyField`` fields aren't supported, because that would entail
+ executing a separate SQL statement for each row in the table. If you
+ want to do this nonetheless, give your model a custom method, and add
+ that method's name to ``list_display``. (See below for more on custom
+ methods in ``list_display``.)
+
+ * If the field is a ``BooleanField`` or ``NullBooleanField``, Django will
+ display a pretty "on" or "off" icon instead of ``True`` or ``False``.
+
+ * If the string given is a method of the model, ``ModelAdmin`` or a
+ callable, Django will HTML-escape the output by default. If you'd rather
+ not escape the output of the method, give the method an ``allow_tags``
+ attribute whose value is ``True``.
+
+ Here's a full example model::
+
+ class Person(models.Model):
+ first_name = models.CharField(max_length=50)
+ last_name = models.CharField(max_length=50)
+ color_code = models.CharField(max_length=6)
+
+ def colored_name(self):
+ return '<span style="color: #%s;">%s %s</span>' % (self.color_code, self.first_name, self.last_name)
+ colored_name.allow_tags = True
+
+ class PersonAdmin(admin.ModelAdmin):
+ list_display = ('first_name', 'last_name', 'colored_name')
+
+ * If the string given is a method of the model, ``ModelAdmin`` or a
+ callable that returns True or False Django will display a pretty "on" or
+ "off" icon if you give the method a ``boolean`` attribute whose value is
+ ``True``.
+
+ Here's a full example model::
+
+ class Person(models.Model):
+ first_name = models.CharField(max_length=50)
+ birthday = models.DateField()
+
+ def born_in_fifties(self):
+ return self.birthday.strftime('%Y')[:3] == '195'
+ born_in_fifties.boolean = True
+
+ class PersonAdmin(admin.ModelAdmin):
+ list_display = ('name', 'born_in_fifties')
+
+
+ * The ``__str__()`` and ``__unicode__()`` methods are just as valid in
+ ``list_display`` as any other model method, so it's perfectly OK to do
+ this::
+
+ list_display = ('__unicode__', 'some_other_field')
+
+ * Usually, elements of ``list_display`` that aren't actual database fields
+ can't be used in sorting (because Django does all the sorting at the
+ database level).
+
+ However, if an element of ``list_display`` represents a certain database
+ field, you can indicate this fact by setting the ``admin_order_field``
+ attribute of the item.
+
+ For example::
+
+ class Person(models.Model):
+ first_name = models.CharField(max_length=50)
+ color_code = models.CharField(max_length=6)
+
+ def colored_first_name(self):
+ return '<span style="color: #%s;">%s</span>' % (self.color_code, self.first_name)
+ colored_first_name.allow_tags = True
+ colored_first_name.admin_order_field = 'first_name'
+
+ class PersonAdmin(admin.ModelAdmin):
+ list_display = ('first_name', 'colored_first_name')
+
+ The above will tell Django to order by the ``first_name`` field when
+ trying to sort by ``colored_first_name`` in the admin.
+
+.. attribute:: ModelAdmin.list_display_links
+
+Set ``list_display_links`` to control which fields in ``list_display`` should
+be linked to the "change" page for an object.
+
+By default, the change list page will link the first column -- the first field
+specified in ``list_display`` -- to the change page for each item. But
+``list_display_links`` lets you change which columns are linked. Set
+``list_display_links`` to a list or tuple of field names (in the same format as
+``list_display``) to link.
+
+``list_display_links`` can specify one or many field names. As long as the
+field names appear in ``list_display``, Django doesn't care how many (or how
+few) fields are linked. The only requirement is: If you want to use
+``list_display_links``, you must define ``list_display``.
+
+In this example, the ``first_name`` and ``last_name`` fields will be linked on
+the change list page::
+
+ class PersonAdmin(admin.ModelAdmin):
+ list_display = ('first_name', 'last_name', 'birthday')
+ list_display_links = ('first_name', 'last_name')
+
+.. _admin-list-editable:
+
+.. attribute:: ModelAdmin.list_editable
+
+.. versionadded:: 1.1
+
+Set ``list_editable`` to a list of field names on the model which will allow
+editing on the change list page. That is, fields listed in ``list_editable``
+will be displayed as form widgets on the change list page, allowing users to
+edit and save multiple rows at once.
+
+.. note::
+
+ ``list_editable`` interacts with a couple of other options in particular
+ ways; you should note the following rules:
+
+ * Any field in ``list_editable`` must also be in ``list_display``. You
+ can't edit a field that's not displayed!
+
+ * The same field can't be listed in both ``list_editable`` and
+ ``list_display_links`` -- a field can't be both a form and a link.
+
+ You'll get a validation error if either of these rules are broken.
+
+.. attribute:: ModelAdmin.list_filter
+
+Set ``list_filter`` to activate filters in the right sidebar of the change list
+page of the admin. This should be a list of field names, and each specified
+field should be either a ``BooleanField``, ``CharField``, ``DateField``,
+``DateTimeField``, ``IntegerField`` or ``ForeignKey``.
+
+This example, taken from the ``django.contrib.auth.models.User`` model, shows
+how both ``list_display`` and ``list_filter`` work::
+
+ class UserAdmin(admin.ModelAdmin):
+ list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff')
+ list_filter = ('is_staff', 'is_superuser')
+
+The above code results in an admin change list page that looks like this:
+
+ .. image:: _images/users_changelist.png
+
+(This example also has ``search_fields`` defined. See below.)
+
+.. attribute:: ModelAdmin.list_per_page
+
+Set ``list_per_page`` to control how many items appear on each paginated admin
+change list page. By default, this is set to ``100``.
+
+.. attribute:: ModelAdmin.list_select_related
+
+Set ``list_select_related`` to tell Django to use
+:meth:`~django.db.models.QuerySet.select_related` in retrieving the list of
+objects on the admin change list page. This can save you a bunch of database
+queries.
+
+The value should be either ``True`` or ``False``. Default is ``False``.
+
+Note that Django will use :meth:`~django.db.models.QuerySet.select_related`,
+regardless of this setting, if one of the ``list_display`` fields is a
+``ForeignKey``.
+
+.. attribute:: ModelAdmin.inlines
+
+See ``InlineModelAdmin`` objects below.
+
+.. attribute:: ModelAdmin.ordering
+
+Set ``ordering`` to specify how objects on the admin change list page should be
+ordered. This should be a list or tuple in the same format as a model's
+``ordering`` parameter.
+
+If this isn't provided, the Django admin will use the model's default ordering.
+
+.. admonition:: Note
+
+ Django will only honor the first element in the list/tuple; any others
+ will be ignored.
+
+.. attribute:: ModelAdmin.prepopulated_fields
+
+Set ``prepopulated_fields`` to a dictionary mapping field names to the fields
+it should prepopulate from::
+
+ class ArticleAdmin(admin.ModelAdmin):
+ prepopulated_fields = {"slug": ("title",)}
+
+When set, the given fields will use a bit of JavaScript to populate from the
+fields assigned. The main use for this functionality is to automatically
+generate the value for ``SlugField`` fields from one or more other fields. The
+generated value is produced by concatenating the values of the source fields,
+and then by transforming that result into a valid slug (e.g. substituting
+dashes for spaces).
+
+``prepopulated_fields`` doesn't accept ``DateTimeField``, ``ForeignKey``, nor
+``ManyToManyField`` fields.
+
+.. attribute:: ModelAdmin.radio_fields
+
+By default, Django's admin uses a select-box interface (<select>) for
+fields that are ``ForeignKey`` or have ``choices`` set. If a field is present
+in ``radio_fields``, Django will use a radio-button interface instead.
+Assuming ``group`` is a ``ForeignKey`` on the ``Person`` model::
+
+ class PersonAdmin(admin.ModelAdmin):
+ radio_fields = {"group": admin.VERTICAL}
+
+You have the choice of using ``HORIZONTAL`` or ``VERTICAL`` from the
+``django.contrib.admin`` module.
+
+Don't include a field in ``radio_fields`` unless it's a ``ForeignKey`` or has
+``choices`` set.
+
+.. attribute:: ModelAdmin.raw_id_fields
+
+By default, Django's admin uses a select-box interface (<select>) for
+fields that are ``ForeignKey``. Sometimes you don't want to incur the
+overhead of having to select all the related instances to display in the
+drop-down.
+
+``raw_id_fields`` is a list of fields you would like to change
+into a ``Input`` widget for either a ``ForeignKey`` or ``ManyToManyField``::
+
+ class ArticleAdmin(admin.ModelAdmin):
+ raw_id_fields = ("newspaper",)
+
+.. attribute:: ModelAdmin.readonly_fields
+
+.. versionadded:: 1.2
+
+By default the admin shows all fields as editable. Any fields in this option
+(which should be a ``list`` or ``tuple``) will display its data as-is and
+non-editable. This option behaves nearly identical to :attr:`ModelAdmin.list_display`.
+Usage is the same, however, when you specify :attr:`ModelAdmin.fields` or
+:attr:`ModelAdmin.fieldsets` the read-only fields must be present to be shown
+(they are ignored otherwise).
+
+If ``readonly_fields`` is used without defining explicit ordering through
+:attr:`ModelAdmin.fields` or :attr:`ModelAdmin.fieldsets` they will be added
+last after all editable fields.
+
+.. attribute:: ModelAdmin.save_as
+
+Set ``save_as`` to enable a "save as" feature on admin change forms.
+
+Normally, objects have three save options: "Save", "Save and continue editing"
+and "Save and add another". If ``save_as`` is ``True``, "Save and add another"
+will be replaced by a "Save as" button.
+
+"Save as" means the object will be saved as a new object (with a new ID),
+rather than the old object.
+
+By default, ``save_as`` is set to ``False``.
+
+.. attribute:: ModelAdmin.save_on_top
+
+Set ``save_on_top`` to add save buttons across the top of your admin change
+forms.
+
+Normally, the save buttons appear only at the bottom of the forms. If you set
+``save_on_top``, the buttons will appear both on the top and the bottom.
+
+By default, ``save_on_top`` is set to ``False``.
+
+.. attribute:: ModelAdmin.search_fields
+
+Set ``search_fields`` to enable a search box on the admin change list page.
+This should be set to a list of field names that will be searched whenever
+somebody submits a search query in that text box.
+
+These fields should be some kind of text field, such as ``CharField`` or
+``TextField``. You can also perform a related lookup on a ``ForeignKey`` or
+``ManyToManyField`` with the lookup API "follow" notation::
+
+ search_fields = ['foreign_key__related_fieldname']
+
+For example, if you have a blog entry with an author, the following definition
+would enable search blog entries by the email address of the author::
+
+ search_fields = ['user__email']
+
+When somebody does a search in the admin search box, Django splits the search
+query into words and returns all objects that contain each of the words, case
+insensitive, where each word must be in at least one of ``search_fields``. For
+example, if ``search_fields`` is set to ``['first_name', 'last_name']`` and a
+user searches for ``john lennon``, Django will do the equivalent of this SQL
+``WHERE`` clause::
+
+ WHERE (first_name ILIKE '%john%' OR last_name ILIKE '%john%')
+ AND (first_name ILIKE '%lennon%' OR last_name ILIKE '%lennon%')
+
+For faster and/or more restrictive searches, prefix the field name
+with an operator:
+
+``^``
+ Matches the beginning of the field. For example, if ``search_fields`` is
+ set to ``['^first_name', '^last_name']`` and a user searches for
+ ``john lennon``, Django will do the equivalent of this SQL ``WHERE``
+ clause::
+
+ WHERE (first_name ILIKE 'john%' OR last_name ILIKE 'john%')
+ AND (first_name ILIKE 'lennon%' OR last_name ILIKE 'lennon%')
+
+ This query is more efficient than the normal ``'%john%'`` query, because
+ the database only needs to check the beginning of a column's data, rather
+ than seeking through the entire column's data. Plus, if the column has an
+ index on it, some databases may be able to use the index for this query,
+ even though it's a ``LIKE`` query.
+
+``=``
+ Matches exactly, case-insensitive. For example, if
+ ``search_fields`` is set to ``['=first_name', '=last_name']`` and
+ a user searches for ``john lennon``, Django will do the equivalent
+ of this SQL ``WHERE`` clause::
+
+ WHERE (first_name ILIKE 'john' OR last_name ILIKE 'john')
+ AND (first_name ILIKE 'lennon' OR last_name ILIKE 'lennon')
+
+ Note that the query input is split by spaces, so, following this example,
+ it's currently not possible to search for all records in which
+ ``first_name`` is exactly ``'john winston'`` (containing a space).
+
+``@``
+ Performs a full-text match. This is like the default search method but uses
+ an index. Currently this is only available for MySQL.
+
+.. attribute:: ModelAdmin.formfield_overrides
+
+.. versionadded:: 1.1
+
+This provides a quick-and-dirty way to override some of the
+:class:`~django.forms.Field` options for use in the admin.
+``formfield_overrides`` is a dictionary mapping a field class to a dict of
+arguments to pass to the field at construction time.
+
+Since that's a bit abstract, let's look at a concrete example. The most common
+use of ``formfield_overrides`` is to add a custom widget for a certain type of
+field. So, imagine we've written a ``RichTextEditorWidget`` that we'd like to
+use for large text fields instead of the default ``<textarea>``. Here's how we'd
+do that::
+
+ from django.db import models
+ from django.contrib import admin
+
+ # Import our custom widget and our model from where they're defined
+ from myapp.widgets import RichTextEditorWidget
+ from myapp.models import MyModel
+
+ class MyModelAdmin(admin.ModelAdmin):
+ formfield_overrides = {
+ models.TextField: {'widget': RichTextEditorWidget},
+ }
+
+Note that the key in the dictionary is the actual field class, *not* a string.
+The value is another dictionary; these arguments will be passed to
+:meth:`~django.forms.Field.__init__`. See :doc:`/ref/forms/api` for details.
+
+.. warning::
+
+ If you want to use a custom widget with a relation field (i.e.
+ :class:`~django.db.models.ForeignKey` or
+ :class:`~django.db.models.ManyToManyField`), make sure you haven't included
+ that field's name in ``raw_id_fields`` or ``radio_fields``.
+
+ ``formfield_overrides`` won't let you change the widget on relation fields
+ that have ``raw_id_fields`` or ``radio_fields`` set. That's because
+ ``raw_id_fields`` and ``radio_fields`` imply custom widgets of their own.
+
+.. attribute:: ModelAdmin.actions
+
+.. versionadded:: 1.1
+
+A list of actions to make available on the change list page. See
+:doc:`/ref/contrib/admin/actions` for details.
+
+.. attribute:: ModelAdmin.actions_on_top
+.. attribute:: ModelAdmin.actions_on_bottom
+
+.. versionadded:: 1.1
+
+Controls where on the page the actions bar appears. By default, the admin
+changelist displays actions at the top of the page (``actions_on_top = True;
+actions_on_bottom = False``).
+
+.. attribute:: ModelAdmin.actions_selection_counter
+
+.. versionadded:: 1.2
+
+Controls whether a selection counter is display next to the action dropdown.
+By default, the admin changelist will display it
+(``actions_selection_counter = True``).
+
+Custom template options
+~~~~~~~~~~~~~~~~~~~~~~~
+
+The `Overriding Admin Templates`_ section describes how to override or extend
+the default admin templates. Use the following options to override the default
+templates used by the :class:`ModelAdmin` views:
+
+.. attribute:: ModelAdmin.add_form_template
+
+ .. versionadded:: 1.2
+
+ Path to a custom template, used by :meth:`add_view`.
+
+.. attribute:: ModelAdmin.change_form_template
+
+ Path to a custom template, used by :meth:`change_view`.
+
+.. attribute:: ModelAdmin.change_list_template
+
+ Path to a custom template, used by :meth:`changelist_view`.
+
+.. attribute:: ModelAdmin.delete_confirmation_template
+
+ Path to a custom template, used by :meth:`delete_view` for displaying a
+ confirmation page when deleting one or more objects.
+
+.. attribute:: ModelAdmin.delete_selected_confirmation_template
+
+ .. versionadded:: 1.2
+
+ Path to a custom template, used by the :meth:`delete_selected`
+ action method for displaying a confirmation page when deleting one
+ or more objects. See the :doc:`actions
+ documentation</ref/contrib/admin/actions>`.
+
+.. attribute:: ModelAdmin.object_history_template
+
+ Path to a custom template, used by :meth:`history_view`.
+
+
+.. _model-admin-methods:
+
+``ModelAdmin`` methods
+----------------------
+
+.. method:: ModelAdmin.save_model(self, request, obj, form, change)
+
+The ``save_model`` method is given the ``HttpRequest``, a model instance,
+a ``ModelForm`` instance and a boolean value based on whether it is adding or
+changing the object. Here you can do any pre- or post-save operations.
+
+For example to attach ``request.user`` to the object prior to saving::
+
+ class ArticleAdmin(admin.ModelAdmin):
+ def save_model(self, request, obj, form, change):
+ obj.user = request.user
+ obj.save()
+
+.. method:: ModelAdmin.save_formset(self, request, form, formset, change)
+
+The ``save_formset`` method is given the ``HttpRequest``, the parent
+``ModelForm`` instance and a boolean value based on whether it is adding or
+changing the parent object.
+
+For example to attach ``request.user`` to each changed formset
+model instance::
+
+ class ArticleAdmin(admin.ModelAdmin):
+ def save_formset(self, request, form, formset, change):
+ instances = formset.save(commit=False)
+ for instance in instances:
+ instance.user = request.user
+ instance.save()
+ formset.save_m2m()
+
+.. method:: ModelAdmin.get_readonly_fields(self, request, obj=None)
+
+.. versionadded:: 1.2
+
+The ``get_readonly_fields`` method is given the ``HttpRequest`` and the
+``obj`` being edited (or ``None`` on an add form) and is expected to return a
+``list`` or ``tuple`` of field names that will be displayed as read-only, as
+described above in the :attr:`ModelAdmin.readonly_fields` section.
+
+.. method:: ModelAdmin.get_urls(self)
+
+.. versionadded:: 1.1
+
+The ``get_urls`` method on a ``ModelAdmin`` returns the URLs to be used for
+that ModelAdmin in the same way as a URLconf. Therefore you can extend them as
+documented in :doc:`/topics/http/urls`::
+
+ class MyModelAdmin(admin.ModelAdmin):
+ def get_urls(self):
+ urls = super(MyModelAdmin, self).get_urls()
+ my_urls = patterns('',
+ (r'^my_view/$', self.my_view)
+ )
+ return my_urls + urls
+
+.. note::
+
+ Notice that the custom patterns are included *before* the regular admin
+ URLs: the admin URL patterns are very permissive and will match nearly
+ anything, so you'll usually want to prepend your custom URLs to the built-in
+ ones.
+
+However, the ``self.my_view`` function registered above suffers from two
+problems:
+
+ * It will *not* perform any permission checks, so it will be accessible to
+ the general public.
+ * It will *not* provide any header details to prevent caching. This means if
+ the page retrieves data from the database, and caching middleware is
+ active, the page could show outdated information.
+
+Since this is usually not what you want, Django provides a convenience wrapper
+to check permissions and mark the view as non-cacheable. This wrapper is
+:meth:`AdminSite.admin_view` (i.e. ``self.admin_site.admin_view`` inside a
+``ModelAdmin`` instance); use it like so::
+
+ class MyModelAdmin(admin.ModelAdmin):
+ def get_urls(self):
+ urls = super(MyModelAdmin, self).get_urls()
+ my_urls = patterns('',
+ (r'^my_view/$', self.admin_site.admin_view(self.my_view))
+ )
+ return my_urls + urls
+
+Notice the wrapped view in the fifth line above::
+
+ (r'^my_view/$', self.admin_site.admin_view(self.my_view))
+
+This wrapping will protect ``self.my_view`` from unauthorized access and will
+apply the ``django.views.decorators.cache.never_cache`` decorator to make sure
+it is not cached if the cache middleware is active.
+
+If the page is cacheable, but you still want the permission check to be performed,
+you can pass a ``cacheable=True`` argument to :meth:`AdminSite.admin_view`::
+
+ (r'^my_view/$', self.admin_site.admin_view(self.my_view, cacheable=True))
+
+.. method:: ModelAdmin.formfield_for_foreignkey(self, db_field, request, **kwargs)
+
+.. versionadded:: 1.1
+
+The ``formfield_for_foreignkey`` method on a ``ModelAdmin`` allows you to
+override the default formfield for a foreign key field. For example, to
+return a subset of objects for this foreign key field based on the user::
+
+ class MyModelAdmin(admin.ModelAdmin):
+ def formfield_for_foreignkey(self, db_field, request, **kwargs):
+ if db_field.name == "car":
+ kwargs["queryset"] = Car.objects.filter(owner=request.user)
+ return super(MyModelAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
+
+This uses the ``HttpRequest`` instance to filter the ``Car`` foreign key field
+to only display the cars owned by the ``User`` instance.
+
+.. method:: ModelAdmin.formfield_for_manytomany(self, db_field, request, **kwargs)
+
+.. versionadded:: 1.1
+
+Like the ``formfield_for_foreignkey`` method, the ``formfield_for_manytomany``
+method can be overridden to change the default formfield for a many to many
+field. For example, if an owner can own multiple cars and cars can belong
+to multiple owners -- a many to many relationship -- you could filter the
+``Car`` foreign key field to only display the cars owned by the ``User``::
+
+ class MyModelAdmin(admin.ModelAdmin):
+ def formfield_for_manytomany(self, db_field, request, **kwargs):
+ if db_field.name == "cars":
+ kwargs["queryset"] = Car.objects.filter(owner=request.user)
+ return super(MyModelAdmin, self).formfield_for_manytomany(db_field, request, **kwargs)
+
+.. method:: ModelAdmin.queryset(self, request)
+
+The ``queryset`` method on a ``ModelAdmin`` returns a
+:class:`~django.db.models.QuerySet` of all model instances that can be
+edited by the admin site. One use case for overriding this method is
+to show objects owned by the logged-in user::
+
+ class MyModelAdmin(admin.ModelAdmin):
+ def queryset(self, request):
+ qs = super(MyModelAdmin, self).queryset(request)
+ if request.user.is_superuser:
+ return qs
+ return qs.filter(author=request.user)
+
+.. method:: ModelAdmin.message_user(request, message)
+
+ Sends a message to the user. The default implementation creates a message
+ using the :mod:`django.contrib.messages` backend. See the
+ :ref:`custom ModelAdmin example <custom-admin-action>`.
+
+Other methods
+~~~~~~~~~~~~~
+
+.. method:: ModelAdmin.add_view(self, request, form_url='', extra_context=None)
+
+Django view for the model instance addition page. See note below.
+
+.. method:: ModelAdmin.change_view(self, request, object_id, extra_context=None)
+
+Django view for the model instance edition page. See note below.
+
+.. method:: ModelAdmin.changelist_view(self, request, extra_context=None)
+
+Django view for the model instances change list/actions page. See note below.
+
+.. method:: ModelAdmin.delete_view(self, request, object_id, extra_context=None)
+
+Django view for the model instance(s) deletion confirmation page. See note below.
+
+.. method:: ModelAdmin.history_view(self, request, object_id, extra_context=None)
+
+Django view for the page that shows the modification history for a given model
+instance.
+
+Unlike the hook-type ``ModelAdmin`` methods detailed in the previous section,
+these five methods are in reality designed to be invoked as Django views from
+the admin application URL dispatching handler to render the pages that deal
+with model instances CRUD operations. As a result, completely overriding these
+methods will significantly change the behavior of the admin application.
+
+One common reason for overriding these methods is to augment the context data
+that is provided to the template that renders the view. In the following
+example, the change view is overridden so that the rendered template is
+provided some extra mapping data that would not otherwise be available::
+
+ class MyModelAdmin(admin.ModelAdmin):
+
+ # A template for a very customized change view:
+ change_form_template = 'admin/myapp/extras/openstreetmap_change_form.html'
+
+ def get_osm_info(self):
+ # ...
+
+ def change_view(self, request, object_id, extra_context=None):
+ my_context = {
+ 'osm_data': self.get_osm_info(),
+ }
+ return super(MyModelAdmin, self).change_view(request, object_id,
+ extra_context=my_context)
+
+``ModelAdmin`` media definitions
+--------------------------------
+
+There are times where you would like add a bit of CSS and/or JavaScript to
+the add/change views. This can be accomplished by using a Media inner class
+on your ``ModelAdmin``::
+
+ class ArticleAdmin(admin.ModelAdmin):
+ class Media:
+ css = {
+ "all": ("my_styles.css",)
+ }
+ js = ("my_code.js",)
+
+Keep in mind that this will be prepended with ``MEDIA_URL``. The same rules
+apply as :doc:`regular media definitions on forms </topics/forms/media>`.
+
+Django admin Javascript makes use of the `jQuery`_ library. To avoid
+conflict with user scripts, Django's jQuery is namespaced as
+``django.jQuery``. If you want to use jQuery in your own admin
+JavaScript without including a second copy, you can use the
+``django.jQuery`` object on changelist and add/edit views.
+
+.. _jQuery: http://jquery.com
+
+Adding custom validation to the admin
+-------------------------------------
+
+Adding custom validation of data in the admin is quite easy. The automatic admin
+interface reuses :mod:`django.forms`, and the ``ModelAdmin`` class gives you
+the ability define your own form::
+
+ class ArticleAdmin(admin.ModelAdmin):
+ form = MyArticleAdminForm
+
+``MyArticleAdminForm`` can be defined anywhere as long as you import where
+needed. Now within your form you can add your own custom validation for
+any field::
+
+ class MyArticleAdminForm(forms.ModelForm):
+ class Meta:
+ model = Article
+
+ def clean_name(self):
+ # do something that validates your data
+ return self.cleaned_data["name"]
+
+It is important you use a ``ModelForm`` here otherwise things can break. See the
+:doc:`forms </ref/forms/index>` documentation on :doc:`custom validation
+</ref/forms/validation>` and, more specifically, the
+:ref:`model form validation notes <overriding-modelform-clean-method>` for more
+information.
+
+.. _admin-inlines:
+
+``InlineModelAdmin`` objects
+============================
+
+.. class:: InlineModelAdmin
+
+The admin interface has the ability to edit models on the same page as a
+parent model. These are called inlines. Suppose you have these two models::
+
+ class Author(models.Model):
+ name = models.CharField(max_length=100)
+
+ class Book(models.Model):
+ author = models.ForeignKey(Author)
+ title = models.CharField(max_length=100)
+
+You can edit the books authored by an author on the author page. You add
+inlines to a model by specifying them in a ``ModelAdmin.inlines``::
+
+ class BookInline(admin.TabularInline):
+ model = Book
+
+ class AuthorAdmin(admin.ModelAdmin):
+ inlines = [
+ BookInline,
+ ]
+
+Django provides two subclasses of ``InlineModelAdmin`` and they are:
+
+ * ``TabularInline``
+ * ``StackedInline``
+
+The difference between these two is merely the template used to render them.
+
+``InlineModelAdmin`` options
+-----------------------------
+
+The ``InlineModelAdmin`` class is a subclass of ``ModelAdmin`` so it inherits
+all the same functionality as well as some of its own:
+
+.. attribute:: InlineModelAdmin.model
+
+ The model in which the inline is using. This is required.
+
+.. attribute:: InlineModelAdmin.fk_name
+
+ The name of the foreign key on the model. In most cases this will be dealt
+ with automatically, but ``fk_name`` must be specified explicitly if there
+ are more than one foreign key to the same parent model.
+
+.. attribute:: InlineModelAdmin.formset
+
+ This defaults to ``BaseInlineFormSet``. Using your own formset can give you
+ many possibilities of customization. Inlines are built around
+ :ref:`model formsets <model-formsets>`.
+
+.. attribute:: InlineModelAdmin.form
+
+ The value for ``form`` defaults to ``ModelForm``. This is what is passed
+ through to ``inlineformset_factory`` when creating the formset for this
+ inline.
+
+.. _ref-contrib-admin-inline-extra:
+
+.. attribute:: InlineModelAdmin.extra
+
+
+ This controls the number of extra forms the formset will display in addition
+ to the initial forms. See the
+ :doc:`formsets documentation </topics/forms/formsets>` for more information.
+
+ .. versionadded:: 1.2
+
+ For users with JavaScript-enabled browsers, an "Add another" link is
+ provided to enable any number of additional inlines to be added in addition
+ to those provided as a result of the ``extra`` argument.
+
+ The dynamic link will not appear if the number of currently displayed forms
+ exceeds ``max_num``, or if the user does not have JavaScript enabled.
+
+.. _ref-contrib-admin-inline-max-num:
+
+.. attribute:: InlineModelAdmin.max_num
+
+ This controls the maximum number of forms to show in the inline. This
+ doesn't directly correlate to the number of objects, but can if the value
+ is small enough. See :ref:`model-formsets-max-num` for more information.
+
+.. attribute:: InlineModelAdmin.raw_id_fields
+
+ By default, Django's admin uses a select-box interface (<select>) for
+ fields that are ``ForeignKey``. Sometimes you don't want to incur the
+ overhead of having to select all the related instances to display in the
+ drop-down.
+
+ ``raw_id_fields`` is a list of fields you would like to change into a
+ ``Input`` widget for either a ``ForeignKey`` or ``ManyToManyField``::
+
+ class BookInline(admin.TabularInline):
+ model = Book
+ raw_id_fields = ("pages",)
+
+
+.. attribute:: InlineModelAdmin.template
+
+ The template used to render the inline on the page.
+
+.. attribute:: InlineModelAdmin.verbose_name
+
+ An override to the ``verbose_name`` found in the model's inner ``Meta``
+ class.
+
+.. attribute:: InlineModelAdmin.verbose_name_plural
+
+ An override to the ``verbose_name_plural`` found in the model's inner
+ ``Meta`` class.
+
+.. attribute:: InlineModelAdmin.can_delete
+
+ Specifies whether or not inline objects can be deleted in the inline.
+ Defaults to ``True``.
+
+
+Working with a model with two or more foreign keys to the same parent model
+---------------------------------------------------------------------------
+
+It is sometimes possible to have more than one foreign key to the same model.
+Take this model for instance::
+
+ class Friendship(models.Model):
+ to_person = models.ForeignKey(Person, related_name="friends")
+ from_person = models.ForeignKey(Person, related_name="from_friends")
+
+If you wanted to display an inline on the ``Person`` admin add/change pages
+you need to explicitly define the foreign key since it is unable to do so
+automatically::
+
+ class FriendshipInline(admin.TabularInline):
+ model = Friendship
+ fk_name = "to_person"
+
+ class PersonAdmin(admin.ModelAdmin):
+ inlines = [
+ FriendshipInline,
+ ]
+
+Working with Many-to-Many Models
+--------------------------------
+
+.. versionadded:: 1.2
+
+By default, admin widgets for many-to-many relations will be displayed
+on whichever model contains the actual reference to the ``ManyToManyField``.
+Depending on your ``ModelAdmin`` definition, each many-to-many field in your
+model will be represented by a standard HTML ``<select multiple>``, a
+horizontal or vertical filter, or a ``raw_id_admin`` widget. However, it is
+also possible to to replace these widgets with inlines.
+
+Suppose we have the following models::
+
+ class Person(models.Model):
+ name = models.CharField(max_length=128)
+
+ class Group(models.Model):
+ name = models.CharField(max_length=128)
+ members = models.ManyToManyField(Person, related_name='groups')
+
+If you want to display many-to-many relations using an inline, you can do
+so by defining an ``InlineModelAdmin`` object for the relationship::
+
+ class MembershipInline(admin.TabularInline):
+ model = Group.members.through
+
+ class PersonAdmin(admin.ModelAdmin):
+ inlines = [
+ MembershipInline,
+ ]
+
+ class GroupAdmin(admin.ModelAdmin):
+ inlines = [
+ MembershipInline,
+ ]
+ exclude = ('members',)
+
+There are two features worth noting in this example.
+
+Firstly - the ``MembershipInline`` class references ``Group.members.through``.
+The ``through`` attribute is a reference to the model that manages the
+many-to-many relation. This model is automatically created by Django when you
+define a many-to-many field.
+
+Secondly, the ``GroupAdmin`` must manually exclude the ``members`` field.
+Django displays an admin widget for a many-to-many field on the model that
+defines the relation (in this case, ``Group``). If you want to use an inline
+model to represent the many-to-many relationship, you must tell Django's admin
+to *not* display this widget - otherwise you will end up with two widgets on
+your admin page for managing the relation.
+
+In all other respects, the ``InlineModelAdmin`` is exactly the same as any
+other. You can customize the appearance using any of the normal
+``ModelAdmin`` properties.
+
+Working with Many-to-Many Intermediary Models
+----------------------------------------------
+
+When you specify an intermediary model using the ``through`` argument to a
+``ManyToManyField``, the admin will not display a widget by default. This is
+because each instance of that intermediary model requires more information
+than could be displayed in a single widget, and the layout required for
+multiple widgets will vary depending on the intermediate model.
+
+However, we still want to be able to edit that information inline. Fortunately,
+this is easy to do with inline admin models. Suppose we have the following
+models::
+
+ class Person(models.Model):
+ name = models.CharField(max_length=128)
+
+ class Group(models.Model):
+ name = models.CharField(max_length=128)
+ members = models.ManyToManyField(Person, through='Membership')
+
+ class Membership(models.Model):
+ person = models.ForeignKey(Person)
+ group = models.ForeignKey(Group)
+ date_joined = models.DateField()
+ invite_reason = models.CharField(max_length=64)
+
+The first step in displaying this intermediate model in the admin is to
+define an inline class for the ``Membership`` model::
+
+ class MembershipInline(admin.TabularInline):
+ model = Membership
+ extra = 1
+
+This simple example uses the default ``InlineModelAdmin`` values for the
+``Membership`` model, and limits the extra add forms to one. This could be
+customized using any of the options available to ``InlineModelAdmin`` classes.
+
+Now create admin views for the ``Person`` and ``Group`` models::
+
+ class PersonAdmin(admin.ModelAdmin):
+ inlines = (MembershipInline,)
+
+ class GroupAdmin(admin.ModelAdmin):
+ inlines = (MembershipInline,)
+
+Finally, register your ``Person`` and ``Group`` models with the admin site::
+
+ admin.site.register(Person, PersonAdmin)
+ admin.site.register(Group, GroupAdmin)
+
+Now your admin site is set up to edit ``Membership`` objects inline from
+either the ``Person`` or the ``Group`` detail pages.
+
+Using generic relations as an inline
+------------------------------------
+
+It is possible to use an inline with generically related objects. Let's say
+you have the following models::
+
+ class Image(models.Model):
+ image = models.ImageField(upload_to="images")
+ content_type = models.ForeignKey(ContentType)
+ object_id = models.PositiveIntegerField()
+ content_object = generic.GenericForeignKey("content_type", "object_id")
+
+ class Product(models.Model):
+ name = models.CharField(max_length=100)
+
+If you want to allow editing and creating ``Image`` instance on the ``Product``
+add/change views you can simply use ``GenericInlineModelAdmin`` provided by
+``django.contrib.contenttypes.generic``. In your ``admin.py`` for this
+example app::
+
+ from django.contrib import admin
+ from django.contrib.contenttypes import generic
+
+ from myproject.myapp.models import Image, Product
+
+ class ImageInline(generic.GenericTabularInline):
+ model = Image
+
+ class ProductAdmin(admin.ModelAdmin):
+ inlines = [
+ ImageInline,
+ ]
+
+ admin.site.register(Product, ProductAdmin)
+
+``django.contrib.contenttypes.generic`` provides both a ``GenericTabularInline``
+and ``GenericStackedInline`` and behave just like any other inline. See the
+:doc:`contenttypes documentation </ref/contrib/contenttypes>` for more specific
+information.
+
+Overriding Admin Templates
+==========================
+
+It is relatively easy to override many of the templates which the admin module
+uses to generate the various pages of an admin site. You can even override a few
+of these templates for a specific app, or a specific model.
+
+Set up your projects admin template directories
+-----------------------------------------------
+
+The admin template files are located in the ``contrib/admin/templates/admin``
+directory.
+
+In order to override one or more of them, first create an ``admin`` directory in
+your project's ``templates`` directory. This can be any of the directories you
+specified in ``TEMPLATE_DIRS``.
+
+Within this ``admin`` directory, create sub-directories named after your app.
+Within these app subdirectories create sub-directories named after your models.
+Note, that the admin app will lowercase the model name when looking for the
+directory, so make sure you name the directory in all lowercase if you are going
+to run your app on a case-sensitive filesystem.
+
+To override an admin template for a specific app, copy and edit the template
+from the ``django/contrib/admin/templates/admin`` directory, and save it to one
+of the directories you just created.
+
+For example, if we wanted to add a tool to the change list view for all the
+models in an app named ``my_app``, we would copy
+``contrib/admin/templates/admin/change_list.html`` to the
+``templates/admin/my_app/`` directory of our project, and make any necessary
+changes.
+
+If we wanted to add a tool to the change list view for only a specific model
+named 'Page', we would copy that same file to the
+``templates/admin/my_app/page`` directory of our project.
+
+Overriding vs. replacing an admin template
+------------------------------------------
+
+Because of the modular design of the admin templates, it is usually neither
+necessary nor advisable to replace an entire template. It is almost always
+better to override only the section of the template which you need to change.
+
+To continue the example above, we want to add a new link next to the ``History``
+tool for the ``Page`` model. After looking at ``change_form.html`` we determine
+that we only need to override the ``object-tools`` block. Therefore here is our
+new ``change_form.html`` :
+
+.. code-block:: html+django
+
+ {% extends "admin/change_form.html" %}
+ {% load i18n %}
+ {% block object-tools %}
+ {% if change %}{% if not is_popup %}
+ <ul class="object-tools">
+ <li><a href="history/" class="historylink">{% trans "History" %}</a></li>
+ <li><a href="mylink/" class="historylink">My Link</a></li>
+ {% if has_absolute_url %}
+ <li><a href="../../../r/{{ content_type_id }}/{{ object_id }}/" class="viewsitelink">
+ {% trans "View on site" %}</a>
+ </li>
+ {% endif%}
+ </ul>
+ {% endif %}{% endif %}
+ {% endblock %}
+
+And that's it! If we placed this file in the ``templates/admin/my_app``
+directory, our link would appear on every model's change form.
+
+Templates which may be overridden per app or model
+--------------------------------------------------
+
+Not every template in ``contrib/admin/templates/admin`` may be overridden per
+app or per model. The following can:
+
+ * ``app_index.html``
+ * ``change_form.html``
+ * ``change_list.html``
+ * ``delete_confirmation.html``
+ * ``object_history.html``
+
+For those templates that cannot be overridden in this way, you may still
+override them for your entire project. Just place the new version in your
+``templates/admin`` directory. This is particularly useful to create custom 404
+and 500 pages.
+
+.. note::
+
+ Some of the admin templates, such as ``change_list_request.html`` are used
+ to render custom inclusion tags. These may be overridden, but in such cases
+ you are probably better off creating your own version of the tag in question
+ and giving it a different name. That way you can use it selectively.
+
+Root and login templates
+------------------------
+
+If you wish to change the index, login or logout templates, you are better off
+creating your own ``AdminSite`` instance (see below), and changing the
+:attr:`AdminSite.index_template` , :attr:`AdminSite.login_template` or
+:attr:`AdminSite.logout_template` properties.
+
+``AdminSite`` objects
+=====================
+
+.. class:: AdminSite(name=None)
+
+A Django administrative site is represented by an instance of
+``django.contrib.admin.sites.AdminSite``; by default, an instance of
+this class is created as ``django.contrib.admin.site`` and you can
+register your models and ``ModelAdmin`` instances with it.
+
+If you'd like to set up your own administrative site with custom
+behavior, however, you're free to subclass ``AdminSite`` and override
+or add anything you like. Then, simply create an instance of your
+``AdminSite`` subclass (the same way you'd instantiate any other
+Python class), and register your models and ``ModelAdmin`` subclasses
+with it instead of using the default.
+
+.. versionadded:: 1.1
+
+When constructing an instance of an ``AdminSite``, you are able to provide
+a unique instance name using the ``name`` argument to the constructor. This
+instance name is used to identify the instance, especially when
+:ref:`reversing admin URLs <admin-reverse-urls>`. If no instance name is
+provided, a default instance name of ``admin`` will be used.
+
+``AdminSite`` attributes
+------------------------
+
+Templates can override or extend base admin templates as described in
+`Overriding Admin Templates`_.
+
+.. attribute:: AdminSite.index_template
+
+Path to a custom template that will be used by the admin site main index view.
+
+.. attribute:: AdminSite.login_template
+
+Path to a custom template that will be used by the admin site login view.
+
+.. attribute:: AdminSite.logout_template
+
+.. versionadded:: 1.2
+
+Path to a custom template that will be used by the admin site logout view.
+
+.. attribute:: AdminSite.password_change_template
+
+.. versionadded:: 1.2
+
+Path to a custom template that will be used by the admin site password change
+view.
+
+.. attribute:: AdminSite.password_change_done_template
+
+.. versionadded:: 1.2
+
+Path to a custom template that will be used by the admin site password change
+done view.
+
+Hooking ``AdminSite`` instances into your URLconf
+-------------------------------------------------
+
+The last step in setting up the Django admin is to hook your ``AdminSite``
+instance into your URLconf. Do this by pointing a given URL at the
+``AdminSite.urls`` method.
+
+In this example, we register the default ``AdminSite`` instance
+``django.contrib.admin.site`` at the URL ``/admin/`` ::
+
+ # urls.py
+ from django.conf.urls.defaults import *
+ from django.contrib import admin
+
+ admin.autodiscover()
+
+ urlpatterns = patterns('',
+ (r'^admin/', include(admin.site.urls)),
+ )
+
+Above we used ``admin.autodiscover()`` to automatically load the
+``INSTALLED_APPS`` admin.py modules.
+
+In this example, we register the ``AdminSite`` instance
+``myproject.admin.admin_site`` at the URL ``/myadmin/`` ::
+
+ # urls.py
+ from django.conf.urls.defaults import *
+ from myproject.admin import admin_site
+
+ urlpatterns = patterns('',
+ (r'^myadmin/', include(admin_site.urls)),
+ )
+
+There is really no need to use autodiscover when using your own ``AdminSite``
+instance since you will likely be importing all the per-app admin.py modules
+in your ``myproject.admin`` module.
+
+Multiple admin sites in the same URLconf
+----------------------------------------
+
+It's easy to create multiple instances of the admin site on the same
+Django-powered Web site. Just create multiple instances of ``AdminSite`` and
+root each one at a different URL.
+
+.. versionchanged:: 1.1
+ The method for hooking ``AdminSite`` instances into urls has changed in
+ Django 1.1.
+
+In this example, the URLs ``/basic-admin/`` and ``/advanced-admin/`` feature
+separate versions of the admin site -- using the ``AdminSite`` instances
+``myproject.admin.basic_site`` and ``myproject.admin.advanced_site``,
+respectively::
+
+ # urls.py
+ from django.conf.urls.defaults import *
+ from myproject.admin import basic_site, advanced_site
+
+ urlpatterns = patterns('',
+ (r'^basic-admin/', include(basic_site.urls)),
+ (r'^advanced-admin/', include(advanced_site.urls)),
+ )
+
+``AdminSite`` instances take a single argument to their constructor, their
+name, which can be anything you like. This argument becomes the prefix to the
+URL names for the purposes of :ref:`reversing them<admin-reverse-urls>`. This
+is only necessary if you are using more than one ``AdminSite``.
+
+Adding views to admin sites
+---------------------------
+
+.. versionadded:: 1.1
+
+Just like :class:`ModelAdmin`, :class:`AdminSite` provides a
+:meth:`~django.contrib.admin.ModelAdmin.get_urls()` method
+that can be overridden to define additional views for the site. To add
+a new view to your admin site, extend the base
+:meth:`~django.contrib.admin.ModelAdmin.get_urls()` method to include
+a pattern for your new view.
+
+.. note::
+ Any view you render that uses the admin templates, or extends the base
+ admin template, should provide the ``current_app`` argument to
+ ``RequestContext`` or ``Context`` when rendering the template. It should
+ be set to either ``self.name`` if your view is on an ``AdminSite`` or
+ ``self.admin_site.name`` if your view is on a ``ModelAdmin``.
+
+.. _admin-reverse-urls:
+
+Reversing Admin URLs
+====================
+
+.. versionadded:: 1.1
+
+When an :class:`AdminSite` is deployed, the views provided by that site are
+accessible using Django's :ref:`URL reversing system <naming-url-patterns>`.
+
+The :class:`AdminSite` provides the following named URL patterns:
+
+ ====================== ======================== =============
+ Page URL name Parameters
+ ====================== ======================== =============
+ Index ``index``
+ Logout ``logout``
+ Password change ``password_change``
+ Password change done ``password_change_done``
+ i18n javascript ``jsi18n``
+ Application index page ``app_list`` ``app_label``
+ ====================== ======================== =============
+
+Each :class:`ModelAdmin` instance provides an additional set of named URLs:
+
+ ====================== =============================================== =============
+ Page URL name Parameters
+ ====================== =============================================== =============
+ Changelist ``{{ app_label }}_{{ model_name }}_changelist``
+ Add ``{{ app_label }}_{{ model_name }}_add``
+ History ``{{ app_label }}_{{ model_name }}_history`` ``object_id``
+ Delete ``{{ app_label }}_{{ model_name }}_delete`` ``object_id``
+ Change ``{{ app_label }}_{{ model_name }}_change`` ``object_id``
+ ====================== =============================================== =============
+
+These named URLs are registered with the application namespace ``admin``, and
+with an instance namespace corresponding to the name of the Site instance.
+
+So - if you wanted to get a reference to the Change view for a particular
+``Choice`` object (from the polls application) in the default admin, you would
+call::
+
+ >>> from django.core import urlresolvers
+ >>> c = Choice.objects.get(...)
+ >>> change_url = urlresolvers.reverse('admin:polls_choice_change', args=(c.id,))
+
+This will find the first registered instance of the admin application (whatever the instance
+name), and resolve to the view for changing ``poll.Choice`` instances in that instance.
+
+If you want to find a URL in a specific admin instance, provide the name of that instance
+as a ``current_app`` hint to the reverse call. For example, if you specifically wanted
+the admin view from the admin instance named ``custom``, you would need to call::
+
+ >>> change_url = urlresolvers.reverse('custom:polls_choice_change', args=(c.id,))
+
+For more details, see the documentation on :ref:`reversing namespaced URLs
+<topics-http-reversing-url-namespaces>`.