You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1054 lines
37 KiB
1054 lines
37 KiB
.. _ref-contrib-admin: |
|
|
|
===================== |
|
The Django 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 five steps in activating the Django admin site: |
|
|
|
1. Add ``django.contrib.admin`` to your ``INSTALLED_APPS`` setting. |
|
|
|
2. Determine which of your application's models should be editable in the |
|
admin interface. |
|
|
|
3. For each of those models, optionally create a ``ModelAdmin`` class that |
|
encapsulates the customized admin functionality and options for that |
|
particular model. |
|
|
|
4. Instantiate an ``AdminSite`` and tell it about each of your models and |
|
``ModelAdmin`` classes. |
|
|
|
5. Hook the ``AdminSite`` instance into your URLconf. |
|
|
|
``ModelAdmin`` objects |
|
====================== |
|
|
|
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 the ``ModelAdmin``:: |
|
|
|
from django.contrib import admin |
|
from myproject.myapp.models import Author |
|
|
|
class AuthorAdmin(admin.ModelAdmin): |
|
pass |
|
admin.site.register(Author, AuthorAdmin) |
|
|
|
``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' |
|
|
|
``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' |
|
|
|
``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`_. |
|
|
|
``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'), |
|
} |
|
|
|
* ``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. |
|
|
|
``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. |
|
|
|
.. 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. |
|
|
|
``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. |
|
|
|
``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. |
|
|
|
``filter_vertical`` |
|
~~~~~~~~~~~~~~~~~~~ |
|
|
|
Same as ``filter_horizontal``, but is a vertical display of the filter |
|
interface. |
|
|
|
``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] == 5 |
|
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. |
|
|
|
``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') |
|
|
|
Finally, note that in order to use ``list_display_links``, you must define |
|
``list_display``, too. |
|
|
|
``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.) |
|
|
|
``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``. |
|
|
|
``list_select_related`` |
|
~~~~~~~~~~~~~~~~~~~~~~~ |
|
|
|
Set ``list_select_related`` to tell Django to use ``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 ``select_related()``, regardless of this setting, |
|
if one of the ``list_display`` fields is a ``ForeignKey``. |
|
|
|
For more on ``select_related()``, see |
|
:ref:`the select_related() docs <select-related>`. |
|
|
|
``inlines`` |
|
~~~~~~~~~~~ |
|
|
|
See ``InlineModelAdmin`` objects below. |
|
|
|
``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. |
|
|
|
``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. |
|
|
|
``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. |
|
|
|
``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",) |
|
|
|
``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``. |
|
|
|
``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``. |
|
|
|
``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`` with |
|
the lookup API "follow" notation:: |
|
|
|
search_fields = ['foreign_key__related_fieldname'] |
|
|
|
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. |
|
|
|
``ModelAdmin`` methods |
|
---------------------- |
|
|
|
``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() |
|
|
|
``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() |
|
|
|
``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 :ref:`regular media definitions on forms <topics-forms-media>`. |
|
|
|
Adding custom validation to the admin |
|
------------------------------------- |
|
|
|
Adding custom validation of data in the admin is quite easy. The automatic admin |
|
interfaces 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 |
|
:ref:`forms <ref-forms-index>` documentation on :ref:`custom validation |
|
<ref-forms-validation>` for more information. |
|
|
|
.. _admin-inlines: |
|
|
|
``InlineModelAdmin`` objects |
|
============================ |
|
|
|
The admin interface has the ability to edit models on the same page as a |
|
parent model. These are called inlines. You can add them to a model by |
|
specifying them in a ``ModelAdmin.inlines`` attribute:: |
|
|
|
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: |
|
|
|
``model`` |
|
~~~~~~~~~ |
|
|
|
The model in which the inline is using. This is required. |
|
|
|
``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. |
|
|
|
``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>`. |
|
|
|
``form`` |
|
~~~~~~~~ |
|
|
|
The value for ``form`` is inherited from ``ModelAdmin``. This is what is |
|
passed through to ``formset_factory`` when creating the formset for this |
|
inline. |
|
|
|
``extra`` |
|
~~~~~~~~~ |
|
|
|
This controls the number of extra forms the formset will display in addition |
|
to the initial forms. See the |
|
:ref:`formsets documentation <topics-forms-formsets>` for more information. |
|
|
|
``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. |
|
|
|
``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",) |
|
|
|
``template`` |
|
~~~~~~~~~~~~ |
|
|
|
The template used to render the inline on the page. |
|
|
|
``verbose_name`` |
|
~~~~~~~~~~~~~~~~ |
|
|
|
An override to the ``verbose_name`` found in the model's inner ``Meta`` class. |
|
|
|
``verbose_name_plural`` |
|
~~~~~~~~~~~~~~~~~~~~~~~ |
|
|
|
An override to the ``verbose_name_plural`` found in the model's inner ``Meta`` |
|
class. |
|
|
|
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 Intermediary Models |
|
---------------------------------------------- |
|
|
|
By default, admin widgets for many-to-many relations will be displayed inline |
|
on whichever model contains the actual reference to the ``ManyToManyField``. |
|
However, 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 |
|
:ref:`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`` :: |
|
|
|
{% 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: |
|
|
|
* ``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 or login templates, you are better off creating |
|
your own ``AdminSite`` instance (see below), and changing the ``index_template`` |
|
or ``login_template`` properties. |
|
|
|
``AdminSite`` objects |
|
===================== |
|
|
|
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. |
|
|
|
|
|
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.root`` 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('', |
|
('^admin/(.*)', admin.site.root), |
|
) |
|
|
|
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('', |
|
('^myadmin/(.*)', admin_site.root), |
|
) |
|
|
|
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. |
|
|
|
Note that the regular expression in the URLpattern *must* group everything in |
|
the URL that comes after the URL root -- hence the ``(.*)`` in these examples. |
|
|
|
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. |
|
|
|
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('', |
|
('^basic-admin/(.*)', basic_site.root), |
|
('^advanced-admin/(.*)', advanced_site.root), |
|
)
|
|
|