mirror of
https://github.com/django/django.git
synced 2024-12-28 20:16:19 +00:00
a323fd3c5e
git-svn-id: http://code.djangoproject.com/svn/django/trunk@13628 bcc190cf-cafb-0310-a4f2-bffc1f526a37
1607 lines
59 KiB
Plaintext
1607 lines
59 KiB
Plaintext
=====================
|
|
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
|
|
|
|
.. 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)
|
|
|
|
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>`.
|