mirror of
				https://github.com/django/django.git
				synced 2025-10-25 14:46:09 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			2045 lines
		
	
	
		
			78 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			2045 lines
		
	
	
		
			78 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.
 | |
| 
 | |
| Overview
 | |
| ========
 | |
| 
 | |
| There are seven steps in activating the Django admin site:
 | |
| 
 | |
| 1. Add ``'django.contrib.admin'`` to your :setting:`INSTALLED_APPS`
 | |
|    setting.
 | |
| 
 | |
| 2. The admin has four dependencies - :mod:`django.contrib.auth`,
 | |
|    :mod:`django.contrib.contenttypes`,
 | |
|    :mod:`django.contrib.messages` and
 | |
|    :mod:`django.contrib.sessions`.  If these applications are not
 | |
|    in your :setting:`INSTALLED_APPS` list, add them.
 | |
| 
 | |
| 3. Add ``django.contrib.messages.context_processors.messages`` to
 | |
|    :setting:`TEMPLATE_CONTEXT_PROCESSORS` and
 | |
|    :class:`~django.contrib.messages.middleware.MessageMiddleware` to
 | |
|    :setting:`MIDDLEWARE_CLASSES`. (These are both active by default, so
 | |
|    you only need to do this if you've manually tweaked the settings.)
 | |
| 
 | |
| 4. Determine which of your application's models should be editable in the
 | |
|    admin interface.
 | |
| 
 | |
| 5. For each of those models, optionally create a ``ModelAdmin`` class that
 | |
|    encapsulates the customized admin functionality and options for that
 | |
|    particular model.
 | |
| 
 | |
| 6. Instantiate an ``AdminSite`` and tell it about each of your models and
 | |
|    ``ModelAdmin`` classes.
 | |
| 
 | |
| 7. Hook the ``AdminSite`` instance into your URLconf.
 | |
| 
 | |
| After you've taken these steps, you'll be able to use your Django admin site
 | |
| by visiting the URL you hooked it into (``/admin/``, by default).
 | |
| 
 | |
| Other topics
 | |
| ------------
 | |
| 
 | |
| .. toctree::
 | |
|    :maxdepth: 1
 | |
| 
 | |
|    actions
 | |
|    admindocs
 | |
| 
 | |
| .. seealso::
 | |
| 
 | |
|     For information about serving the static files (images, JavaScript, and
 | |
|     CSS) associated with the admin in production, see :ref:`serving-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.actions
 | |
| 
 | |
|     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
 | |
| 
 | |
|     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``).
 | |
| 
 | |
| .. 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'
 | |
| 
 | |
|     .. versionadded:: 1.3
 | |
| 
 | |
|     This will intelligently populate itself based on available data,
 | |
|     e.g. if all the dates are in one month, it'll show the day-level
 | |
|     drill-down only.
 | |
| 
 | |
| .. 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.fields
 | |
| 
 | |
|     If you need to achieve simple changes in the layout of fields in the forms
 | |
|     of the "add" and "change" pages like only showing a subset of the available
 | |
|     fields, modifying their order or grouping them in rows you can use the
 | |
|     ``fields`` option (for more complex layout needs see the
 | |
|     :attr:`~ModelAdmin.fieldsets` option described in the next section). 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.
 | |
| 
 | |
|     .. versionadded:: 1.4
 | |
| 
 | |
|     To display multiple fields on the same line, wrap those fields in their own
 | |
|     tuple. In this example, the ``url`` and ``title`` fields will display on the
 | |
|     same line and the ``content`` field will be displayed below them in its
 | |
|     own line::
 | |
| 
 | |
|         class FlatPageAdmin(admin.ModelAdmin):
 | |
|             fields = (('url', 'title'), 'content')
 | |
| 
 | |
|     .. admonition:: Note
 | |
| 
 | |
|         This ``fields`` option should not be confused with the ``fields``
 | |
|         dictionary key that is within the :attr:`~ModelAdmin.fieldsets` option,
 | |
|         as described in the next section.
 | |
| 
 | |
|     If neither ``fields`` nor :attr:`~ModelAdmin.fieldsets` options are present,
 | |
|     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.
 | |
| 
 | |
| .. 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 :class:`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 neither ``fieldsets`` nor :attr:`~ModelAdmin.fields` options are present,
 | |
|     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'),
 | |
|             }
 | |
| 
 | |
|         Just like with the :attr:`~ModelAdmin.fields` option, 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.filter_horizontal
 | |
| 
 | |
|     By default, a :class:`~django.db.models.ManyToManyField` is displayed in
 | |
|     the admin site with a ``<select multiple>``. However, multiple-select boxes
 | |
|     can be difficult to use when selecting many items. Adding a
 | |
|     :class:`~django.db.models.ManyToManyField` to this list will instead use
 | |
|     a nifty unobtrusive JavaScript "filter" interface that allows searching
 | |
|     within the options. The unselected and selected options appear in two boxes
 | |
|     side by side. See :attr:`~ModelAdmin.filter_vertical` to use a vertical
 | |
|     interface.
 | |
| 
 | |
| .. attribute:: ModelAdmin.filter_vertical
 | |
| 
 | |
|     Same as :attr:`~ModelAdmin.filter_horizontal`, but uses a vertical display
 | |
|     of the filter interface with the box of unselected options appearing above
 | |
|     the box of selected options.
 | |
| 
 | |
| .. 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`_.
 | |
| 
 | |
|     .. admonition:: Note
 | |
| 
 | |
|         If your ``ModelForm`` and ``ModelAdmin`` both define an ``exclude``
 | |
|         option then ``ModelAdmin`` takes precedence::
 | |
| 
 | |
|             class PersonForm(forms.ModelForm):
 | |
| 
 | |
|                 class Meta:
 | |
|                     model = Person
 | |
|                     exclude = ['name']
 | |
| 
 | |
|             class PersonAdmin(admin.ModelAdmin):
 | |
|                 exclude = ['age']
 | |
|                 form = PersonForm
 | |
| 
 | |
|         In the above example, the "age" field will be excluded but the "name"
 | |
|         field will be included in the generated form.
 | |
| 
 | |
| .. attribute:: ModelAdmin.formfield_overrides
 | |
| 
 | |
|     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.inlines
 | |
| 
 | |
|     See :class:`InlineModelAdmin` objects below.
 | |
| 
 | |
| .. 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 fields (in the same
 | |
|     format as ``list_display``) to link.
 | |
| 
 | |
|     ``list_display_links`` can specify one or many fields. As long as the
 | |
|     fields 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
 | |
| 
 | |
|     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, as illustrated in the following screenshot:
 | |
| 
 | |
|     .. image:: _images/users_changelist.png
 | |
| 
 | |
|     ``list_filter`` should be a list of elements, where each element should be
 | |
|     of one of the following types:
 | |
| 
 | |
|     * a field name, where the specified field should be either a
 | |
|       ``BooleanField``, ``CharField``, ``DateField``, ``DateTimeField``,
 | |
|       ``IntegerField``, ``ForeignKey`` or ``ManyToManyField``, for example::
 | |
| 
 | |
|           class PersonAdmin(ModelAdmin):
 | |
|               list_filter = ('is_staff', 'company')
 | |
| 
 | |
|       .. versionadded:: 1.3
 | |
| 
 | |
|       Field names in ``list_filter`` can also span relations
 | |
|       using the ``__`` lookup, for example::
 | |
| 
 | |
|           class PersonAdmin(UserAdmin):
 | |
|               list_filter = ('company__name',)
 | |
| 
 | |
|     .. versionadded:: 1.4
 | |
| 
 | |
|     * a class inheriting from :mod:`django.contrib.admin.SimpleListFilter`,
 | |
|       which you need to provide the ``title`` and ``parameter_name``
 | |
|       attributes to and override the ``lookups`` and ``queryset`` methods,
 | |
|       e.g.::
 | |
| 
 | |
|            from datetime import date
 | |
| 
 | |
|            from django.utils.translation import ugettext_lazy as _
 | |
|            from django.contrib.admin import SimpleListFilter
 | |
| 
 | |
|            class DecadeBornListFilter(SimpleListFilter):
 | |
|                # Human-readable title which will be displayed in the
 | |
|                # right admin sidebar just above the filter options.
 | |
|                title = _('decade born')
 | |
| 
 | |
|                # Parameter for the filter that will be used in the URL query.
 | |
|                parameter_name = 'decade'
 | |
| 
 | |
|                def lookups(self, request, model_admin):
 | |
|                    """
 | |
|                    Returns a list of tuples. The first element in each
 | |
|                    tuple is the coded value for the option that will
 | |
|                    appear in the URL query. The second element is the
 | |
|                    human-readable name for the option that will appear
 | |
|                    in the right sidebar.
 | |
|                    """
 | |
|                    return (
 | |
|                        ('80s', _('in the eighties')),
 | |
|                        ('90s', _('in the nineties')),
 | |
|                    )
 | |
| 
 | |
|                def queryset(self, request, queryset):
 | |
|                    """
 | |
|                    Returns the filtered queryset based on the value
 | |
|                    provided in the query string and retrievable via
 | |
|                    `self.value()`.
 | |
|                    """
 | |
|                    # Compare the requested value (either '80s' or 'other')
 | |
|                    # to decide how to filter the queryset.
 | |
|                    if self.value() == '80s':
 | |
|                        return queryset.filter(birthday__gte=date(1980, 1, 1),
 | |
|                                                birthday__lte=date(1989, 12, 31))
 | |
|                    if self.value() == '90s':
 | |
|                        return queryset.filter(birthday__gte=date(1990, 1, 1),
 | |
|                                                birthday__lte=date(1999, 12, 31))
 | |
| 
 | |
|            class PersonAdmin(ModelAdmin):
 | |
|                list_filter = (DecadeBornListFilter,)
 | |
| 
 | |
|       .. note::
 | |
| 
 | |
|           As a convenience, the ``HttpRequest`` object is passed to the
 | |
|           ``lookups`` and ``queryset`` methods, for example::
 | |
| 
 | |
|               class AuthDecadeBornListFilter(DecadeBornListFilter):
 | |
| 
 | |
|                   def lookups(self, request, model_admin):
 | |
|                       if request.user.is_superuser:
 | |
|                           return super(AuthDecadeBornListFilter,
 | |
|                               self).lookups(request, model_admin)
 | |
| 
 | |
|                   def queryset(self, request, queryset):
 | |
|                       if request.user.is_superuser:
 | |
|                           return super(AuthDecadeBornListFilter,
 | |
|                               self).queryset(request, queryset)
 | |
| 
 | |
|           Also as a convenience, the ``ModelAdmin`` object is passed to
 | |
|           the ``lookups`` method, for example if you want to base the
 | |
|           lookups on the available data::
 | |
| 
 | |
|               class AdvancedDecadeBornListFilter(DecadeBornListFilter):
 | |
| 
 | |
|                   def lookups(self, request, model_admin):
 | |
|                       """
 | |
|                       Only show the lookups if there actually is
 | |
|                       anyone born in the corresponding decades.
 | |
|                       """
 | |
|                       qs = model_admin.queryset(request)
 | |
|                       if qs.filter(birthday__gte=date(1980, 1, 1),
 | |
|                                     birthday__lte=date(1989, 12, 31)).exists():
 | |
|                           yield ('80s', _('in the eighties'))
 | |
|                       if qs.filter(birthday__gte=date(1990, 1, 1),
 | |
|                                     birthday__lte=date(1999, 12, 31)).exists():
 | |
|                           yield ('90s', _('in the nineties'))
 | |
| 
 | |
|     .. versionadded:: 1.4
 | |
| 
 | |
|     * a tuple, where the first element is a field name and the second
 | |
|       element is a class inheriting from
 | |
|       :mod:`django.contrib.admin.FieldListFilter`, for example::
 | |
| 
 | |
|           from django.contrib.admin import BooleanFieldListFilter
 | |
| 
 | |
|           class PersonAdmin(ModelAdmin):
 | |
|               list_filter = (
 | |
|                   ('is_staff', BooleanFieldListFilter),
 | |
|               )
 | |
| 
 | |
|       .. note::
 | |
| 
 | |
|         The ``FieldListFilter`` API is considered internal and might be
 | |
|         changed.
 | |
| 
 | |
|     .. versionadded:: 1.4
 | |
| 
 | |
|     It is possible to specify a custom template for rendering a list filter::
 | |
| 
 | |
|         class FilterWithCustomTemplate(SimpleListFilter):
 | |
|             template = "custom_template.html"
 | |
| 
 | |
|     See the default template provided by django (``admin/filter.html``) for
 | |
|     a concrete example.
 | |
| 
 | |
| .. attribute:: ModelAdmin.list_max_show_all
 | |
| 
 | |
|     .. versionadded:: 1.4
 | |
| 
 | |
|     Set ``list_max_show_all`` to control how many items can appear on a "Show
 | |
|     all" admin change list page. The admin will display a "Show all" link on the
 | |
|     change list only if the total result count is less than or equal to this
 | |
|     setting. By default, this is set to ``200``.
 | |
| 
 | |
| .. 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.query.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.query.QuerySet.select_related`,
 | |
|     regardless of this setting if one of the ``list_display`` fields is a
 | |
|     ``ForeignKey``.
 | |
| 
 | |
| .. attribute:: ModelAdmin.ordering
 | |
| 
 | |
|     Set ``ordering`` to specify how lists of objects should be ordered in the
 | |
|     Django admin views. This should be a list or tuple in the same format as a
 | |
|     model's :attr:`~django.db.models.Options.ordering` parameter.
 | |
| 
 | |
|     If this isn't provided, the Django admin will use the model's default
 | |
|     ordering.
 | |
| 
 | |
|     .. versionadded:: 1.4
 | |
| 
 | |
|     If you need to specify a dynamic order (for example depending on user or
 | |
|     language) you can implement a :meth:`~ModelAdmin.get_ordering` method.
 | |
| 
 | |
|     .. versionchanged:: 1.4
 | |
| 
 | |
|     Django honors all elements in the list/tuple; before 1.4, only the first
 | |
|     was respected.
 | |
| 
 | |
| .. attribute:: ModelAdmin.paginator
 | |
| 
 | |
|     .. versionadded:: 1.3
 | |
| 
 | |
|     The paginator class to be used for pagination. By default,
 | |
|     :class:`django.core.paginator.Paginator` is used. If the custom paginator
 | |
|     class doesn't have the same constructor interface as
 | |
|     :class:`django.core.paginator.Paginator`, you will also need to
 | |
|     provide an implementation for :meth:`ModelAdmin.get_paginator`.
 | |
| 
 | |
| .. 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 an ``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.
 | |
| 
 | |
| 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
 | |
| ----------------------
 | |
| 
 | |
| .. warning::
 | |
| 
 | |
|     :meth:`ModelAdmin.save_model` and :meth:`ModelAdmin.delete_model` must
 | |
|     save/delete the object, they are not for veto purposes, rather they allow
 | |
|     you to perform extra operations.
 | |
| 
 | |
| .. 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.delete_model(self, request, obj)
 | |
| 
 | |
|     .. versionadded:: 1.3
 | |
| 
 | |
|     The ``delete_model`` method is given the ``HttpRequest`` and a model
 | |
|     instance. Use this method to do pre- or post-delete operations.
 | |
| 
 | |
| .. 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_ordering(self, request)
 | |
| 
 | |
|     .. versionadded:: 1.4
 | |
| 
 | |
|     The ``get_ordering`` method takes a``request`` as parameter and
 | |
|     is expected to return a ``list`` or ``tuple`` for ordering similar
 | |
|     to the :attr:`ordering` attribute. For example::
 | |
| 
 | |
|         class PersonAdmin(ModelAdmin):
 | |
| 
 | |
|             def get_ordering(self, request):
 | |
|                 if request.user.is_superuser:
 | |
|                     return ['name', 'rank']
 | |
|                 else:
 | |
|                     return ['name']
 | |
| 
 | |
| .. method:: ModelAdmin.save_related(self, request, form, formsets, change)
 | |
| 
 | |
|     .. versionadded:: 1.4
 | |
| 
 | |
|     The ``save_related`` method is given the ``HttpRequest``, the parent
 | |
|     ``ModelForm`` instance, the list of inline formsets and a boolean value
 | |
|     based on whether the parent is being added or changed. Here you can do any
 | |
|     pre- or post-save operations for objects related to the parent. Note
 | |
|     that at this point the parent object and its form have already been saved.
 | |
| 
 | |
| .. 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_prepopulated_fields(self, request, obj=None)
 | |
| 
 | |
|     .. versionadded:: 1.4
 | |
| 
 | |
|     The ``get_prepopulated_fields`` method is given the ``HttpRequest`` and the
 | |
|     ``obj`` being edited (or ``None`` on an add form) and is expected to return
 | |
|     a ``dictionary``, as described above in the :attr:`ModelAdmin.prepopulated_fields`
 | |
|     section.
 | |
| 
 | |
| .. method:: ModelAdmin.get_list_display(self, request)
 | |
| 
 | |
|     .. versionadded:: 1.4
 | |
| 
 | |
|     The ``get_list_display`` method is given the ``HttpRequest`` and is
 | |
|     expected to return a ``list`` or ``tuple`` of field names that will be
 | |
|     displayed on the changelist view as described above in the
 | |
|     :attr:`ModelAdmin.list_display` section.
 | |
| 
 | |
| .. method:: ModelAdmin.get_list_display_links(self, request, list_display)
 | |
| 
 | |
|     .. versionadded:: 1.4
 | |
| 
 | |
|     The ``get_list_display_links`` method is given the ``HttpRequest`` and
 | |
|     the ``list`` or ``tuple`` returned by :meth:`ModelAdmin.get_list_display`.
 | |
|     It is expected to return a ``list`` or ``tuple`` of field names on the
 | |
|     changelist that will be linked to the change view, as described in the
 | |
|     :attr:`ModelAdmin.list_display_links` section.
 | |
| 
 | |
| .. method:: ModelAdmin.get_urls(self)
 | |
| 
 | |
|     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
 | |
| 
 | |
|             def my_view(self, request):
 | |
|                 # custom view which should return an HttpResponse
 | |
|                 pass
 | |
| 
 | |
|     .. 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.
 | |
| 
 | |
|         In this example, ``my_view`` will be accessed at
 | |
|         ``/admin/myapp/mymodel/my_view/`` (assuming the admin URLs are included
 | |
|         at ``/admin/``.)
 | |
| 
 | |
|     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)
 | |
| 
 | |
|     The ``formfield_for_foreignkey`` method on a ``ModelAdmin`` allows you to
 | |
|     override the default formfield for a foreign keys 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)
 | |
| 
 | |
|     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.formfield_for_choice_field(self, db_field, request, **kwargs)
 | |
| 
 | |
|     Like the ``formfield_for_foreignkey`` and ``formfield_for_manytomany``
 | |
|     methods, the ``formfield_for_choice_field`` method can be overridden to
 | |
|     change the default formfield for a field that has declared choices. For
 | |
|     example, if the choices available to a superuser should be different than
 | |
|     those available to regular staff, you could proceed as follows::
 | |
| 
 | |
|         class MyModelAdmin(admin.ModelAdmin):
 | |
|             def formfield_for_choice_field(self, db_field, request, **kwargs):
 | |
|                 if db_field.name == "status":
 | |
|                     kwargs['choices'] = (
 | |
|                         ('accepted', 'Accepted'),
 | |
|                         ('denied', 'Denied'),
 | |
|                     )
 | |
|                     if request.user.is_superuser:
 | |
|                         kwargs['choices'] += (('ready', 'Ready for deployment'),)
 | |
|                 return super(MyModelAdmin, self).formfield_for_choice_field(db_field, request, **kwargs)
 | |
| 
 | |
| .. method:: ModelAdmin.get_changelist(self, request, **kwargs)
 | |
| 
 | |
|     Returns the Changelist class to be used for listing. By default,
 | |
|     ``django.contrib.admin.views.main.ChangeList`` is used. By inheriting this
 | |
|     class you can change the behavior of the listing.
 | |
| 
 | |
| .. method:: ModelAdmin.has_add_permission(self, request)
 | |
| 
 | |
|     Should return ``True`` if adding an object is permitted, ``False``
 | |
|     otherwise.
 | |
| 
 | |
| .. method:: ModelAdmin.has_change_permission(self, request, obj=None)
 | |
| 
 | |
|     Should return ``True`` if editing obj is permitted, ``False`` otherwise.
 | |
|     If obj is ``None``, should return ``True`` or ``False`` to indicate whether
 | |
|     editing of objects of this type is permitted in general (e.g., ``False``
 | |
|     will be interpreted as meaning that the current user is not permitted to
 | |
|     edit any object of this type).
 | |
| 
 | |
| .. method:: ModelAdmin.has_delete_permission(self, request, obj=None)
 | |
| 
 | |
|     Should return ``True`` if deleting obj is permitted, ``False`` otherwise.
 | |
|     If obj is ``None``, should return ``True`` or ``False`` to indicate whether
 | |
|     deleting objects of this type is permitted in general (e.g., ``False`` will
 | |
|     be interpreted as meaning that the current user is not permitted to delete
 | |
|     any object of this type).
 | |
| 
 | |
| .. method:: ModelAdmin.queryset(self, request)
 | |
| 
 | |
|     The ``queryset`` method on a ``ModelAdmin`` returns a
 | |
|     :class:`~django.db.models.query.QuerySet` of all model instances that
 | |
|     can be edited by the admin site. One use case for overriding this method
 | |
|     is to show objects owned by the logged-in user::
 | |
| 
 | |
|         class MyModelAdmin(admin.ModelAdmin):
 | |
|             def queryset(self, request):
 | |
|                 qs = super(MyModelAdmin, self).queryset(request)
 | |
|                 if request.user.is_superuser:
 | |
|                     return qs
 | |
|                 return qs.filter(author=request.user)
 | |
| 
 | |
| .. method:: ModelAdmin.message_user(request, message)
 | |
| 
 | |
|     Sends a message to the user. The default implementation creates a message
 | |
|     using the :mod:`django.contrib.messages` backend. See the
 | |
|     :ref:`custom ModelAdmin example <custom-admin-action>`.
 | |
| 
 | |
| .. method:: ModelAdmin.get_paginator(queryset, per_page, orphans=0, allow_empty_first_page=True)
 | |
| 
 | |
|     .. versionadded:: 1.3
 | |
| 
 | |
|     Returns an instance of the paginator to use for this view. By default,
 | |
|     instantiates an instance of :attr:`paginator`.
 | |
| 
 | |
| 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, form_url='', extra_context=None)
 | |
| 
 | |
|     Django view for the model instance edition page. See note below.
 | |
| 
 | |
|     .. versionchanged:: 1.4
 | |
| 
 | |
|     The ``form_url`` parameter was added.
 | |
| 
 | |
| .. 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):
 | |
|             # ...
 | |
|             pass
 | |
| 
 | |
|         def change_view(self, request, object_id, form_url='', extra_context=None):
 | |
|             extra_context = extra_context or {}
 | |
|             extra_context['osm_data'] = self.get_osm_info()
 | |
|             return super(MyModelAdmin, self).change_view(request, object_id,
 | |
|                 form_url, extra_context=extra_context)
 | |
| 
 | |
| .. versionadded:: 1.4
 | |
| 
 | |
| These views now return :class:`~django.template.response.TemplateResponse`
 | |
| instances which allow you to easily customize the response data before
 | |
| rendering. For more details, see the
 | |
| :doc:`TemplateResponse documentation </ref/template-response>`.
 | |
| 
 | |
| ``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",)
 | |
| 
 | |
| .. versionchanged:: 1.3
 | |
| 
 | |
| The :doc:`staticfiles app </ref/contrib/staticfiles>` prepends
 | |
| :setting:`STATIC_URL` (or :setting:`MEDIA_URL` if :setting:`STATIC_URL` is
 | |
| ``None``) to any media paths. The same rules apply as :ref:`regular media
 | |
| definitions on forms <form-media-paths>`.
 | |
| 
 | |
| Django admin Javascript makes use of the `jQuery`_ library. To avoid
 | |
| conflicts with user-supplied scripts or libraries, 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.
 | |
| 
 | |
| If you require the jQuery library to be in the global namespace, for example
 | |
| when using third-party jQuery plugins, or need a newer version of jQuery, you
 | |
| will have to include your own copy of jQuery.
 | |
| 
 | |
| .. _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
 | |
| .. class:: TabularInline
 | |
| .. class:: StackedInline
 | |
| 
 | |
|     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:
 | |
| 
 | |
|     * :class:`~django.contrib.admin.TabularInline`
 | |
|     * :class:`~django.contrib.admin.StackedInline`
 | |
| 
 | |
|     The difference between these two is merely the template used to render
 | |
|     them.
 | |
| 
 | |
| ``InlineModelAdmin`` options
 | |
| -----------------------------
 | |
| 
 | |
| ``InlineModelAdmin`` shares many of the same features as ``ModelAdmin``, and
 | |
| adds some of its own (the shared features are actually defined in the
 | |
| ``BaseModelAdmin`` superclass). The shared features are:
 | |
| 
 | |
| - :attr:`~InlineModelAdmin.form`
 | |
| - :attr:`~ModelAdmin.fieldsets`
 | |
| - :attr:`~ModelAdmin.fields`
 | |
| - :attr:`~ModelAdmin.exclude`
 | |
| - :attr:`~ModelAdmin.filter_horizontal`
 | |
| - :attr:`~ModelAdmin.filter_vertical`
 | |
| - :attr:`~ModelAdmin.prepopulated_fields`
 | |
| - :attr:`~ModelAdmin.radio_fields`
 | |
| - :attr:`~InlineModelAdmin.raw_id_fields`
 | |
| - :meth:`~ModelAdmin.formfield_for_foreignkey`
 | |
| - :meth:`~ModelAdmin.formfield_for_manytomany`
 | |
| 
 | |
| .. versionadded:: 1.2
 | |
| 
 | |
| - :attr:`~ModelAdmin.readonly_fields`
 | |
| - :attr:`~ModelAdmin.formfield_overrides`
 | |
| 
 | |
| .. versionadded:: 1.3
 | |
| 
 | |
| - :attr:`~ModelAdmin.ordering`
 | |
| - :meth:`~ModelAdmin.queryset`
 | |
| 
 | |
| .. versionadded:: 1.4
 | |
| 
 | |
| - :meth:`~ModelAdmin.has_add_permission`
 | |
| - :meth:`~ModelAdmin.has_change_permission`
 | |
| - :meth:`~ModelAdmin.has_delete_permission`
 | |
| 
 | |
| The ``InlineModelAdmin`` class adds:
 | |
| 
 | |
| .. attribute:: InlineModelAdmin.model
 | |
| 
 | |
|     The model 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
 | |
| :class:`~django.db.models.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 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
 | |
| :class:`~django.db.models.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:
 | |
| 
 | |
| 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 use ``GenericTabularInline`` or
 | |
| ``GenericStackedInline`` (both subclasses of ``GenericInlineModelAdmin``)
 | |
| provided by ``django.contrib.contenttypes.generic``, they implement tabular and
 | |
| stacked visual layouts for the forms representing the inline objects
 | |
| respectively just like their non-generic counterparts and behave just like any
 | |
| other inline. 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)
 | |
| 
 | |
| 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 :setting:`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='admin')
 | |
| 
 | |
|     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.
 | |
| 
 | |
|     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.login_form
 | |
| 
 | |
|     .. versionadded:: 1.3
 | |
| 
 | |
|     Subclass of :class:`~django.contrib.auth.forms.AuthenticationForm` 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 import patterns, url, include
 | |
|     from django.contrib import admin
 | |
| 
 | |
|     admin.autodiscover()
 | |
| 
 | |
|     urlpatterns = patterns('',
 | |
|         (r'^admin/', include(admin.site.urls)),
 | |
|     )
 | |
| 
 | |
| Above we used ``admin.autodiscover()`` to automatically load the
 | |
| :setting:`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 import patterns, url, include
 | |
|     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.
 | |
| 
 | |
| 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 import patterns, url, include
 | |
|     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
 | |
| ---------------------------
 | |
| 
 | |
| 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
 | |
|     :class:`~django.template.RequestContext` or
 | |
|     :class:`~django.template.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``.
 | |
| 
 | |
| .. _auth_password_reset:
 | |
| 
 | |
| Adding a password-reset feature
 | |
| -------------------------------
 | |
| 
 | |
| You can add a password-reset feature to the admin site by adding a few lines to
 | |
| your URLconf. Specifically, add these four patterns:
 | |
| 
 | |
| .. code-block:: python
 | |
| 
 | |
|     url(r'^admin/password_reset/$', 'django.contrib.auth.views.password_reset', name='admin_password_reset'),
 | |
|     (r'^admin/password_reset/done/$', 'django.contrib.auth.views.password_reset_done'),
 | |
|     (r'^reset/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$', 'django.contrib.auth.views.password_reset_confirm'),
 | |
|     (r'^reset/done/$', 'django.contrib.auth.views.password_reset_complete'),
 | |
| 
 | |
| (This assumes you've added the admin at ``admin/`` and requires that you put
 | |
| the URLs starting with ``^admin/`` before the line that includes the admin app
 | |
| itself).
 | |
| 
 | |
| .. versionchanged:: 1.4
 | |
| 
 | |
| The presence of the ``admin_password_reset`` named URL will cause a "forgotten
 | |
| your password?" link to appear on the default admin log-in page under the
 | |
| password box.
 | |
| 
 | |
| .. _admin-reverse-urls:
 | |
| 
 | |
| Reversing admin URLs
 | |
| ====================
 | |
| 
 | |
| 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>`.
 | |
| 
 | |
| .. versionadded:: 1.4
 | |
| 
 | |
| To allow easier reversing of the admin urls in templates, Django provides an
 | |
| ``admin_urlname`` filter which takes an action as argument:
 | |
| 
 | |
| .. code-block:: html+django
 | |
| 
 | |
|     {% load admin_urls %}
 | |
|     {% load url from future %}
 | |
|     <a href="{% url opts|admin_urlname:'add' %}">Add user</a>
 | |
|     <a href="{% url opts|admin_urlname:'delete' user.pk %}">Delete this user</a>
 | |
| 
 | |
| The action in the examples above match the last part of the URL names for
 | |
| :class:`ModelAdmin` instances described above. The ``opts`` variable can be any
 | |
| object which has an ``app_label`` and ``module_name`` and is usually supplied
 | |
| by the admin views for the current model.
 |