2008-07-18 23:54:34 +00:00
|
|
|
|
=====================
|
|
|
|
|
The Django admin site
|
|
|
|
|
=====================
|
|
|
|
|
|
2008-09-17 04:52:25 +00:00
|
|
|
|
.. module:: django.contrib.admin
|
|
|
|
|
:synopsis: Django's admin site.
|
|
|
|
|
|
2008-07-18 23:54:34 +00:00
|
|
|
|
One of the most powerful parts of Django is the automatic admin interface. It
|
2016-02-08 21:46:40 +00:00
|
|
|
|
reads metadata from your models to provide a quick, model-centric interface
|
|
|
|
|
where trusted users can manage content on your site. The admin's recommended
|
|
|
|
|
use is limited to an organization's internal management tool. It's not intended
|
|
|
|
|
for building your entire front end around.
|
|
|
|
|
|
|
|
|
|
The admin has many hooks for customization, but beware of trying to use those
|
|
|
|
|
hooks exclusively. If you need to provide a more process-centric interface
|
|
|
|
|
that abstracts away the implementation details of database tables and fields,
|
|
|
|
|
then it's probably time to write your own views.
|
|
|
|
|
|
|
|
|
|
In this document we discuss how to activate, use, and customize Django's admin
|
|
|
|
|
interface.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
|
|
|
|
Overview
|
|
|
|
|
========
|
|
|
|
|
|
Simplified default project template.
Squashed commit of:
commit 508ec9144b35c50794708225b496bde1eb5e60aa
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Tue Jan 29 22:50:55 2013 +0100
Tweaked default settings file.
* Explained why BASE_DIR exists.
* Added a link to the database configuration options, and put it in its
own section.
* Moved sensitive settings that must be changed for production at the
top.
commit 6515fd2f1aa73a86dc8dbd2ccf512ddb6b140d57
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Tue Jan 29 14:35:21 2013 +0100
Documented the simplified app & project templates in the changelog.
commit 2c5b576c2ea91d84273a019b3d0b3b8b4da72f23
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Tue Jan 29 13:59:27 2013 +0100
Minor fixes in tutorials 5 and 6.
commit 55a51531be8104f21b3cca3f6bf70b0a7139a041
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Tue Jan 29 13:51:11 2013 +0100
Updated tutorial 2 for the new project template.
commit 29ddae87bdaecff12dd31b16b000c01efbde9e20
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Tue Jan 29 11:58:54 2013 +0100
Updated tutorial 1 for the new project template.
commit 0ecb9f6e2514cfd26a678a280d471433375101a3
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Tue Jan 29 11:29:13 2013 +0100
Adjusted the default URLconf detection to account for the admin.
It's now enabled by default.
commit 5fb4da0d3d09dac28dd94e3fde92b9d4335c0565
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Tue Jan 29 10:36:55 2013 +0100
Added security warnings for the most sensitive settings.
commit 718d84bd8ac4a42fb4b28ec93965de32680f091e
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 23:24:06 2013 +0100
Used an absolute path for the SQLite database.
This ensures the settings file works regardless of which directory
django-admin.py / manage.py is invoked from.
BASE_DIR got a +1 from a BDFL and another core dev. It doesn't involve
the concept of a "Django project"; it's just a convenient way to express
relative paths within the source code repository for non-Python files.
Thanks Jacob Kaplan-Moss for the suggestion.
commit 1b559b4bcda622e10909b68fe5cab90db6727dd9
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 23:22:40 2013 +0100
Removed STATIC_ROOT from the default settings template.
It isn't necessary in development, and it confuses beginners to no end.
Thanks Carl Meyer for the suggestion.
commit a55f141a500bb7c9a1bc259bbe1954c13b199671
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 23:21:43 2013 +0100
Removed MEDIA_ROOT/URL from default settings template.
Many sites will never deal with user-uploaded files, and MEDIA_ROOT is
complicated to explain.
Thanks Carl Meyer for the suggestion.
commit 44bf2f2441420fd9429ee9fe1f7207f92dd87e70
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 22:22:09 2013 +0100
Removed logging config.
This configuration is applied regardless of the value of LOGGING;
duplicating it in LOGGING is confusing.
commit eac747e848eaed65fd5f6f254f0a7559d856f88f
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 22:05:31 2013 +0100
Enabled the locale middleware by default.
USE_I18N is True by default, and doesn't work well without
LocaleMiddleware.
commit d806c62b2d00826dc2688c84b092627b8d571cab
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 22:03:16 2013 +0100
Enabled clickjacking protection by default.
commit 99152c30e6a15003f0b6737dc78e87adf462aacb
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 22:01:48 2013 +0100
Reorganized settings in logical sections, and trimmed comments.
commit d37ffdfcb24b7e0ec7cc113d07190f65fb12fb8a
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 16:54:11 2013 +0100
Avoided misleading TEMPLATE_DEBUG = DEBUG.
According to the docs TEMPLATE_DEBUG works only when DEBUG = True.
commit 15d9478d3a9850e85841e7cf09cf83050371c6bf
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 16:46:25 2013 +0100
Removed STATICFILES_FINDERS/TEMPLATE_LOADERS from default settings file.
Only developers with special needs ever need to change these settings.
commit 574da0eb5bfb4570883756914b4dbd7e20e1f61e
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 16:45:01 2013 +0100
Removed STATICFILES/TEMPLATES_DIRS from default settings file.
The current best practice is to put static files and templates in
applications, for easier testing and deployment.
commit 8cb18dbe56629aa1be74718a07e7cc66b4f9c9f0
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 16:24:16 2013 +0100
Removed settings related to email reporting from default settings file.
While handy for small scale projects, it isn't exactly a best practice.
commit 8ecbfcb3638058f0c49922540f874a7d802d864f
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Tue Jan 29 18:54:43 2013 +0100
Documented how to enable the sites framework.
commit 23fc91a6fa67d91ddd9d71b1c3e0dc26bdad9841
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 16:28:59 2013 +0100
Disabled the sites framework by default.
RequestSite does the job for single-domain websites.
commit c4d82eb8afc0eb8568bf9c4d12644272415e3960
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Tue Jan 29 00:08:33 2013 +0100
Added a default admin.py to the application template.
Thanks Ryan D Hiebert for the suggestion.
commit 4071dc771e5c44b1c5ebb9beecefb164ae465e22
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 10:59:49 2013 +0100
Enabled the admin by default.
Everyone uses the admin.
commit c807a31f8d89e7e7fd97380e3023f7983a8b6fcb
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 10:57:05 2013 +0100
Removed admindocs from default project template.
commit 09e4ce0e652a97da1a9e285046a91c8ad7a9189c
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 16:32:52 2013 +0100
Added links to the settings documentation.
commit 5b8f5eaef364eb790fcde6f9e86f7d266074cca8
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 11:06:54 2013 +0100
Used a significant example for URLconf includes.
commit 908e91d6fcee2a3cb51ca26ecdf12a6a24e69ef8
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 16:22:31 2013 +0100
Moved code comments about WSGI to docs, and rewrote said docs.
commit 50417e51996146f891d08ca8b74dcc736a581932
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date: Mon Jan 28 15:51:50 2013 +0100
Normalized the default application template.
Removed the default test that 1 + 1 = 2, because it's been committed
way too many times, in too many projects.
Added an import of `render` for views, because the first view will
often be:
def home(request):
return render(request, "mysite/home.html")
2013-01-28 14:51:50 +00:00
|
|
|
|
The admin is enabled in the default project template used by
|
|
|
|
|
:djadmin:`startproject`.
|
|
|
|
|
|
|
|
|
|
For reference, here are the requirements:
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2014-01-24 21:43:00 +00:00
|
|
|
|
1. Add ``'django.contrib.admin'`` to your :setting:`INSTALLED_APPS` setting.
|
2008-07-27 18:36:08 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
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.
|
2009-09-12 23:56:45 +00:00
|
|
|
|
|
2015-06-27 18:25:42 +00:00
|
|
|
|
3. Add ``django.contrib.auth.context_processors.auth`` and
|
|
|
|
|
``django.contrib.messages.context_processors.messages`` to
|
2014-12-17 22:36:32 +00:00
|
|
|
|
the ``'context_processors'`` option of the ``DjangoTemplates`` backend
|
|
|
|
|
defined in your :setting:`TEMPLATES` as well as
|
2012-11-02 20:15:40 +00:00
|
|
|
|
:class:`django.contrib.auth.middleware.AuthenticationMiddleware` and
|
|
|
|
|
:class:`django.contrib.messages.middleware.MessageMiddleware` to
|
2015-11-07 15:12:37 +00:00
|
|
|
|
:setting:`MIDDLEWARE`. These are all active by default, so you only need to
|
|
|
|
|
do this if you've manually tweaked the settings.
|
2011-04-07 22:01:23 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
4. Determine which of your application's models should be editable in the
|
|
|
|
|
admin interface.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
5. For each of those models, optionally create a ``ModelAdmin`` class that
|
|
|
|
|
encapsulates the customized admin functionality and options for that
|
|
|
|
|
particular model.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
6. Instantiate an ``AdminSite`` and tell it about each of your models and
|
|
|
|
|
``ModelAdmin`` classes.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
7. Hook the ``AdminSite`` instance into your URLconf.
|
2009-03-24 11:41:37 +00:00
|
|
|
|
|
2011-12-09 20:46:10 +00:00
|
|
|
|
After you've taken these steps, you'll be able to use your Django admin site
|
2016-01-30 04:33:48 +00:00
|
|
|
|
by visiting the URL you hooked it into (``/admin/``, by default). If you need
|
|
|
|
|
to create a user to login with, you can use the :djadmin:`createsuperuser`
|
|
|
|
|
command.
|
2011-12-09 20:46:10 +00:00
|
|
|
|
|
2009-03-23 20:22:56 +00:00
|
|
|
|
Other topics
|
|
|
|
|
------------
|
|
|
|
|
|
|
|
|
|
.. toctree::
|
|
|
|
|
:maxdepth: 1
|
2009-03-24 11:41:37 +00:00
|
|
|
|
|
2009-03-23 20:22:56 +00:00
|
|
|
|
actions
|
2010-11-07 09:21:55 +00:00
|
|
|
|
admindocs
|
2015-06-18 16:27:58 +00:00
|
|
|
|
javascript
|
2009-04-16 12:46:58 +00:00
|
|
|
|
|
Fixed a whole bunch of small docs typos, errors, and ommissions.
Fixes #8358, #8396, #8724, #9043, #9128, #9247, #9267, #9267, #9375, #9409, #9414, #9416, #9446, #9454, #9464, #9503, #9518, #9533, #9657, #9658, #9683, #9733, #9771, #9835, #9836, #9837, #9897, #9906, #9912, #9945, #9986, #9992, #10055, #10084, #10091, #10145, #10245, #10257, #10309, #10358, #10359, #10424, #10426, #10508, #10531, #10551, #10635, #10637, #10656, #10658, #10690, #10699, #19528.
Thanks to all the respective authors of those tickets.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@10371 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2009-04-03 18:30:54 +00:00
|
|
|
|
.. seealso::
|
|
|
|
|
|
2011-07-01 15:18:42 +00:00
|
|
|
|
For information about serving the static files (images, JavaScript, and
|
|
|
|
|
CSS) associated with the admin in production, see :ref:`serving-files`.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2012-09-27 21:17:21 +00:00
|
|
|
|
Having problems? Try :doc:`/faq/admin`.
|
|
|
|
|
|
2008-07-18 23:54:34 +00:00
|
|
|
|
``ModelAdmin`` objects
|
|
|
|
|
======================
|
|
|
|
|
|
2009-04-16 12:46:58 +00:00
|
|
|
|
.. class:: ModelAdmin
|
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
The ``ModelAdmin`` class is the representation of a model in the admin
|
2014-01-18 18:34:54 +00:00
|
|
|
|
interface. Usually, these are stored in a file named ``admin.py`` in your
|
2010-12-05 04:52:31 +00:00
|
|
|
|
application. Let's take a look at a very simple example of
|
|
|
|
|
the ``ModelAdmin``::
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
from django.contrib import admin
|
|
|
|
|
from myproject.myapp.models import Author
|
2008-07-27 18:36:08 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
class AuthorAdmin(admin.ModelAdmin):
|
|
|
|
|
pass
|
|
|
|
|
admin.site.register(Author, AuthorAdmin)
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
.. admonition:: Do you need a ``ModelAdmin`` object at all?
|
2008-09-14 08:58:16 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
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::
|
2008-09-14 08:58:16 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
from django.contrib import admin
|
|
|
|
|
from myproject.myapp.models import Author
|
2009-02-22 06:08:13 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
admin.site.register(Author)
|
2009-02-22 06:08:13 +00:00
|
|
|
|
|
2016-01-24 21:26:11 +00:00
|
|
|
|
The ``register`` decorator
|
|
|
|
|
--------------------------
|
2012-12-18 00:04:10 +00:00
|
|
|
|
|
2015-07-27 12:35:21 +00:00
|
|
|
|
.. function:: register(*models, site=django.admin.sites.site)
|
2012-12-18 00:04:10 +00:00
|
|
|
|
|
|
|
|
|
There is also a decorator for registering your ``ModelAdmin`` classes::
|
|
|
|
|
|
|
|
|
|
from django.contrib import admin
|
|
|
|
|
from .models import Author
|
|
|
|
|
|
|
|
|
|
@admin.register(Author)
|
|
|
|
|
class AuthorAdmin(admin.ModelAdmin):
|
|
|
|
|
pass
|
|
|
|
|
|
2017-11-10 02:21:43 +00:00
|
|
|
|
It's given one or more model classes to register with the ``ModelAdmin``.
|
|
|
|
|
If you're using a custom :class:`AdminSite`, pass it using the ``site`` keyword
|
|
|
|
|
argument::
|
2012-12-18 00:04:10 +00:00
|
|
|
|
|
|
|
|
|
from django.contrib import admin
|
2018-05-12 17:37:42 +00:00
|
|
|
|
from .models import Author, Editor, Reader
|
2012-12-18 00:04:10 +00:00
|
|
|
|
from myproject.admin_site import custom_admin_site
|
|
|
|
|
|
|
|
|
|
@admin.register(Author, Reader, Editor, site=custom_admin_site)
|
|
|
|
|
class PersonAdmin(admin.ModelAdmin):
|
|
|
|
|
pass
|
|
|
|
|
|
2015-09-27 00:27:09 +00:00
|
|
|
|
You can't use this decorator if you have to reference your model admin
|
|
|
|
|
class in its ``__init__()`` method, e.g.
|
2017-01-18 16:51:29 +00:00
|
|
|
|
``super(PersonAdmin, self).__init__(*args, **kwargs)``. You can use
|
|
|
|
|
``super().__init__(*args, **kwargs)``.
|
2015-09-27 00:27:09 +00:00
|
|
|
|
|
2014-01-18 18:34:54 +00:00
|
|
|
|
Discovery of admin files
|
|
|
|
|
------------------------
|
|
|
|
|
|
2014-01-24 21:43:00 +00:00
|
|
|
|
When you put ``'django.contrib.admin'`` in your :setting:`INSTALLED_APPS`
|
|
|
|
|
setting, Django automatically looks for an ``admin`` module in each
|
|
|
|
|
application and imports it.
|
2014-01-18 18:34:54 +00:00
|
|
|
|
|
2014-01-20 01:29:09 +00:00
|
|
|
|
.. class:: apps.AdminConfig
|
2014-01-18 18:34:54 +00:00
|
|
|
|
|
2014-01-24 21:43:00 +00:00
|
|
|
|
This is the default :class:`~django.apps.AppConfig` class for the admin.
|
|
|
|
|
It calls :func:`~django.contrib.admin.autodiscover()` when Django starts.
|
|
|
|
|
|
|
|
|
|
.. class:: apps.SimpleAdminConfig
|
|
|
|
|
|
|
|
|
|
This class works like :class:`~django.contrib.admin.apps.AdminConfig`,
|
|
|
|
|
except it doesn't call :func:`~django.contrib.admin.autodiscover()`.
|
2014-01-18 18:34:54 +00:00
|
|
|
|
|
2016-12-29 17:51:26 +00:00
|
|
|
|
.. attribute:: default_site
|
|
|
|
|
|
|
|
|
|
.. versionadded:: 2.1
|
|
|
|
|
|
|
|
|
|
A dotted import path to the default admin site's class or to a callable
|
|
|
|
|
that returns a site instance. Defaults to
|
|
|
|
|
``'django.contrib.admin.sites.AdminSite'``. See
|
|
|
|
|
:ref:`overriding-default-admin-site` for usage.
|
|
|
|
|
|
2014-01-20 01:29:09 +00:00
|
|
|
|
.. function:: autodiscover
|
2014-01-18 18:34:54 +00:00
|
|
|
|
|
|
|
|
|
This function attempts to import an ``admin`` module in each installed
|
|
|
|
|
application. Such modules are expected to register models with the admin.
|
|
|
|
|
|
2015-01-26 20:39:52 +00:00
|
|
|
|
Typically you won't need to call this function directly as
|
|
|
|
|
:class:`~django.contrib.admin.apps.AdminConfig` calls it when Django starts.
|
2014-01-18 18:34:54 +00:00
|
|
|
|
|
|
|
|
|
If you are using a custom ``AdminSite``, it is common to import all of the
|
|
|
|
|
``ModelAdmin`` subclasses into your code and register them to the custom
|
2014-01-24 21:43:00 +00:00
|
|
|
|
``AdminSite``. In that case, in order to disable auto-discovery, you should
|
|
|
|
|
put ``'django.contrib.admin.apps.SimpleAdminConfig'`` instead of
|
|
|
|
|
``'django.contrib.admin'`` in your :setting:`INSTALLED_APPS` setting.
|
|
|
|
|
|
2011-01-08 21:15:00 +00:00
|
|
|
|
``ModelAdmin`` options
|
2008-07-18 23:54:34 +00:00
|
|
|
|
----------------------
|
|
|
|
|
|
|
|
|
|
The ``ModelAdmin`` is very flexible. It has several options for dealing with
|
|
|
|
|
customizing the interface. All options are defined on the ``ModelAdmin``
|
|
|
|
|
subclass::
|
|
|
|
|
|
2013-05-19 10:42:35 +00:00
|
|
|
|
from django.contrib import admin
|
|
|
|
|
|
2008-07-18 23:54:34 +00:00
|
|
|
|
class AuthorAdmin(admin.ModelAdmin):
|
|
|
|
|
date_hierarchy = 'pub_date'
|
|
|
|
|
|
2011-01-08 21:15:00 +00:00
|
|
|
|
.. 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
|
|
|
|
|
|
2012-07-25 08:57:30 +00:00
|
|
|
|
Controls whether a selection counter is displayed next to the action dropdown.
|
2011-01-08 21:15:00 +00:00
|
|
|
|
By default, the admin changelist will display it
|
|
|
|
|
(``actions_selection_counter = True``).
|
|
|
|
|
|
2009-04-16 12:46:58 +00:00
|
|
|
|
.. attribute:: ModelAdmin.date_hierarchy
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
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.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
Example::
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
date_hierarchy = 'pub_date'
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2016-05-05 17:52:54 +00:00
|
|
|
|
You can also specify a field on a related model using the ``__`` lookup,
|
|
|
|
|
for example::
|
|
|
|
|
|
|
|
|
|
date_hierarchy = 'author__pub_date'
|
|
|
|
|
|
2011-01-08 21:15:00 +00:00
|
|
|
|
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.
|
2010-12-12 22:54:50 +00:00
|
|
|
|
|
2013-02-28 16:35:13 +00:00
|
|
|
|
.. note::
|
|
|
|
|
|
|
|
|
|
``date_hierarchy`` uses :meth:`QuerySet.datetimes()
|
|
|
|
|
<django.db.models.query.QuerySet.datetimes>` internally. Please refer
|
|
|
|
|
to its documentation for some caveats when time zone support is
|
|
|
|
|
enabled (:setting:`USE_TZ = True <USE_TZ>`).
|
|
|
|
|
|
2015-03-13 10:08:03 +00:00
|
|
|
|
.. attribute:: ModelAdmin.empty_value_display
|
|
|
|
|
|
|
|
|
|
This attribute overrides the default display value for record's fields that
|
|
|
|
|
are empty (``None``, empty string, etc.). The default value is ``-`` (a
|
|
|
|
|
dash). For example::
|
|
|
|
|
|
|
|
|
|
from django.contrib import admin
|
|
|
|
|
|
|
|
|
|
class AuthorAdmin(admin.ModelAdmin):
|
|
|
|
|
empty_value_display = '-empty-'
|
|
|
|
|
|
|
|
|
|
You can also override ``empty_value_display`` for all admin pages with
|
|
|
|
|
:attr:`AdminSite.empty_value_display`, or for specific fields like this::
|
|
|
|
|
|
|
|
|
|
from django.contrib import admin
|
|
|
|
|
|
|
|
|
|
class AuthorAdmin(admin.ModelAdmin):
|
|
|
|
|
fields = ('name', 'title', 'view_birth_date')
|
|
|
|
|
|
|
|
|
|
def view_birth_date(self, obj):
|
|
|
|
|
return obj.birth_date
|
|
|
|
|
|
|
|
|
|
view_birth_date.empty_value_display = '???'
|
|
|
|
|
|
2011-01-08 21:15:00 +00:00
|
|
|
|
.. attribute:: ModelAdmin.exclude
|
2008-08-04 19:29:33 +00:00
|
|
|
|
|
2011-01-08 21:15:00 +00:00
|
|
|
|
This attribute, if given, should be a list of field names to exclude from
|
|
|
|
|
the form.
|
2008-08-15 21:37:34 +00:00
|
|
|
|
|
2011-01-08 21:15:00 +00:00
|
|
|
|
For example, let's consider the following model::
|
|
|
|
|
|
2013-05-19 10:42:35 +00:00
|
|
|
|
from django.db import models
|
|
|
|
|
|
2011-01-08 21:15:00 +00:00
|
|
|
|
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::
|
|
|
|
|
|
2013-05-19 10:42:35 +00:00
|
|
|
|
from django.contrib import admin
|
|
|
|
|
|
2011-01-08 21:15:00 +00:00
|
|
|
|
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
|
|
|
|
|
|
2015-05-21 15:07:38 +00:00
|
|
|
|
Use the ``fields`` option to make simple layout changes in the forms on
|
|
|
|
|
the "add" and "change" pages such as showing only a subset of available
|
|
|
|
|
fields, modifying their order, or grouping them into rows. For example, you
|
|
|
|
|
could define a simpler version of the admin form for the
|
2013-01-01 13:12:42 +00:00
|
|
|
|
:class:`django.contrib.flatpages.models.FlatPage` model as follows::
|
2011-01-08 21:15:00 +00:00
|
|
|
|
|
|
|
|
|
class FlatPageAdmin(admin.ModelAdmin):
|
|
|
|
|
fields = ('url', 'title', 'content')
|
|
|
|
|
|
2011-05-14 16:29:39 +00:00
|
|
|
|
In the above example, only the fields ``url``, ``title`` and ``content``
|
2012-06-07 13:02:35 +00:00
|
|
|
|
will be displayed, sequentially, in the form. ``fields`` can contain
|
|
|
|
|
values defined in :attr:`ModelAdmin.readonly_fields` to be displayed as
|
|
|
|
|
read-only.
|
2011-01-08 21:15:00 +00:00
|
|
|
|
|
2015-05-21 15:07:38 +00:00
|
|
|
|
For more complex layout needs, see the :attr:`~ModelAdmin.fieldsets` option.
|
|
|
|
|
|
2013-02-25 07:15:11 +00:00
|
|
|
|
The ``fields`` option, unlike :attr:`~ModelAdmin.list_display`, may only
|
|
|
|
|
contain names of fields on the model or the form specified by
|
|
|
|
|
:attr:`~ModelAdmin.form`. It may contain callables only if they are listed
|
|
|
|
|
in :attr:`~ModelAdmin.readonly_fields`.
|
2013-02-23 14:45:34 +00:00
|
|
|
|
|
2011-05-14 16:29:39 +00:00
|
|
|
|
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
|
2015-05-21 15:07:38 +00:00
|
|
|
|
same line and the ``content`` field will be displayed below them on its
|
2011-05-14 16:29:39 +00:00
|
|
|
|
own line::
|
|
|
|
|
|
|
|
|
|
class FlatPageAdmin(admin.ModelAdmin):
|
|
|
|
|
fields = (('url', 'title'), 'content')
|
|
|
|
|
|
2011-01-08 21:15:00 +00:00
|
|
|
|
.. admonition:: Note
|
|
|
|
|
|
|
|
|
|
This ``fields`` option should not be confused with the ``fields``
|
2011-05-14 16:29:39 +00:00
|
|
|
|
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.
|
2008-08-15 21:37:34 +00:00
|
|
|
|
|
2009-04-16 12:46:58 +00:00
|
|
|
|
.. attribute:: ModelAdmin.fieldsets
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
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.
|
|
|
|
|
|
2013-01-01 13:12:42 +00:00
|
|
|
|
A full example, taken from the
|
|
|
|
|
:class:`django.contrib.flatpages.models.FlatPage` model::
|
2010-12-05 04:52:31 +00:00
|
|
|
|
|
2013-05-19 10:42:35 +00:00
|
|
|
|
from django.contrib import admin
|
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
class FlatPageAdmin(admin.ModelAdmin):
|
|
|
|
|
fieldsets = (
|
|
|
|
|
(None, {
|
|
|
|
|
'fields': ('url', 'title', 'content', 'sites')
|
|
|
|
|
}),
|
|
|
|
|
('Advanced options', {
|
|
|
|
|
'classes': ('collapse',),
|
2015-08-28 19:13:41 +00:00
|
|
|
|
'fields': ('registration_required', 'template_name'),
|
2010-12-05 04:52:31 +00:00
|
|
|
|
}),
|
|
|
|
|
)
|
2008-07-27 18:36:08 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
This results in an admin page that looks like:
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2015-09-11 03:07:10 +00:00
|
|
|
|
.. image:: _images/fieldsets.png
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2011-05-14 16:29:39 +00:00
|
|
|
|
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.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
The ``field_options`` dictionary can have the following keys:
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
* ``fields``
|
|
|
|
|
A tuple of field names to display in this fieldset. This key is
|
|
|
|
|
required.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
Example::
|
2009-02-22 06:08:13 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
{
|
|
|
|
|
'fields': ('first_name', 'last_name', 'address', 'city', 'state'),
|
|
|
|
|
}
|
2009-02-22 06:08:13 +00:00
|
|
|
|
|
2013-02-25 07:15:11 +00:00
|
|
|
|
As 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::
|
2009-02-22 06:08:13 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
{
|
|
|
|
|
'fields': (('first_name', 'last_name'), 'address', 'city', 'state'),
|
|
|
|
|
}
|
2008-08-29 17:02:06 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
``fields`` can contain values defined in
|
|
|
|
|
:attr:`~ModelAdmin.readonly_fields` to be displayed as read-only.
|
2009-12-22 18:29:00 +00:00
|
|
|
|
|
2013-02-25 07:15:11 +00:00
|
|
|
|
If you add the name of a callable to ``fields``, the same rule applies
|
|
|
|
|
as with the :attr:`~ModelAdmin.fields` option: the callable must be
|
|
|
|
|
listed in :attr:`~ModelAdmin.readonly_fields`.
|
2013-02-23 14:45:34 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
* ``classes``
|
2015-05-21 15:07:38 +00:00
|
|
|
|
A list or tuple containing extra CSS classes to apply to the fieldset.
|
2009-12-22 18:29:00 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
Example::
|
2009-02-22 06:08:13 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
{
|
2013-02-19 17:44:19 +00:00
|
|
|
|
'classes': ('wide', 'extrapretty'),
|
2011-10-10 17:32:33 +00:00
|
|
|
|
}
|
2008-08-29 17:02:06 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
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.
|
2008-08-29 17:02:06 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
* ``description``
|
|
|
|
|
A string of optional extra text to be displayed at the top of each
|
2014-04-10 17:31:59 +00:00
|
|
|
|
fieldset, under the heading of the fieldset. This string is not
|
|
|
|
|
rendered for :class:`~django.contrib.admin.TabularInline` due to its
|
|
|
|
|
layout.
|
2008-08-29 17:02:06 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
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.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2011-01-08 21:15:00 +00:00
|
|
|
|
.. attribute:: ModelAdmin.filter_horizontal
|
2008-08-01 19:29:37 +00:00
|
|
|
|
|
2011-01-29 02:01:43 +00:00
|
|
|
|
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.
|
2008-08-01 19:29:37 +00:00
|
|
|
|
|
2011-01-08 21:15:00 +00:00
|
|
|
|
.. attribute:: ModelAdmin.filter_vertical
|
2008-08-01 19:29:37 +00:00
|
|
|
|
|
2011-01-29 02:01:43 +00:00
|
|
|
|
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.
|
2009-12-22 18:29:00 +00:00
|
|
|
|
|
2011-01-08 21:15:00 +00:00
|
|
|
|
.. attribute:: ModelAdmin.form
|
2009-12-22 18:29:00 +00:00
|
|
|
|
|
2011-01-08 21:15:00 +00:00
|
|
|
|
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
|
2012-10-19 00:12:41 +00:00
|
|
|
|
on the add/change pages. Alternatively, you can customize the default
|
|
|
|
|
form rather than specifying an entirely new one by using the
|
|
|
|
|
:meth:`ModelAdmin.get_form` method.
|
2008-08-01 19:29:37 +00:00
|
|
|
|
|
2014-10-06 12:59:04 +00:00
|
|
|
|
For an example see the section :ref:`admin-custom-validation`.
|
2008-08-01 19:29:37 +00:00
|
|
|
|
|
2013-02-21 21:56:55 +00:00
|
|
|
|
.. admonition:: Note
|
|
|
|
|
|
|
|
|
|
If you define the ``Meta.model`` attribute on a
|
|
|
|
|
:class:`~django.forms.ModelForm`, you must also define the
|
|
|
|
|
``Meta.fields`` attribute (or the ``Meta.exclude`` attribute). However,
|
|
|
|
|
since the admin has its own way of defining fields, the ``Meta.fields``
|
|
|
|
|
attribute will be ignored.
|
|
|
|
|
|
|
|
|
|
If the ``ModelForm`` is only going to be used for the admin, the easiest
|
|
|
|
|
solution is to omit the ``Meta.model`` attribute, since ``ModelAdmin``
|
|
|
|
|
will provide the correct model to use. Alternatively, you can set
|
|
|
|
|
``fields = []`` in the ``Meta`` class to satisfy the validation on the
|
|
|
|
|
``ModelForm``.
|
|
|
|
|
|
2011-08-12 14:14:49 +00:00
|
|
|
|
.. admonition:: Note
|
|
|
|
|
|
|
|
|
|
If your ``ModelForm`` and ``ModelAdmin`` both define an ``exclude``
|
|
|
|
|
option then ``ModelAdmin`` takes precedence::
|
|
|
|
|
|
2013-05-19 10:42:35 +00:00
|
|
|
|
from django import forms
|
|
|
|
|
from django.contrib import admin
|
|
|
|
|
from myapp.models import Person
|
|
|
|
|
|
2011-08-12 14:14:49 +00:00
|
|
|
|
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.
|
|
|
|
|
|
2011-01-08 21:15:00 +00:00
|
|
|
|
.. attribute:: ModelAdmin.formfield_overrides
|
2008-09-02 17:26:24 +00:00
|
|
|
|
|
2011-01-08 21:15:00 +00:00
|
|
|
|
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.
|
2008-09-02 17:26:24 +00:00
|
|
|
|
|
2011-01-08 21:15:00 +00:00
|
|
|
|
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::
|
2008-09-02 17:26:24 +00:00
|
|
|
|
|
2011-01-08 21:15:00 +00:00
|
|
|
|
from django.contrib import admin
|
2018-05-12 17:37:42 +00:00
|
|
|
|
from django.db import models
|
2008-09-02 17:26:24 +00:00
|
|
|
|
|
2011-01-08 21:15:00 +00:00
|
|
|
|
# Import our custom widget and our model from where they're defined
|
|
|
|
|
from myapp.models import MyModel
|
2018-05-12 17:37:42 +00:00
|
|
|
|
from myapp.widgets import RichTextEditorWidget
|
2009-02-22 06:08:13 +00:00
|
|
|
|
|
2011-01-08 21:15:00 +00:00
|
|
|
|
class MyModelAdmin(admin.ModelAdmin):
|
|
|
|
|
formfield_overrides = {
|
|
|
|
|
models.TextField: {'widget': RichTextEditorWidget},
|
|
|
|
|
}
|
2009-02-22 06:08:13 +00:00
|
|
|
|
|
2011-01-08 21:15:00 +00:00
|
|
|
|
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
|
2013-01-01 13:12:42 +00:00
|
|
|
|
the form field's ``__init__()`` method. See :doc:`/ref/forms/api` for
|
2011-01-08 21:15:00 +00:00
|
|
|
|
details.
|
2008-09-02 17:26:24 +00:00
|
|
|
|
|
2011-01-08 21:15:00 +00:00
|
|
|
|
.. warning::
|
2008-09-02 17:26:24 +00:00
|
|
|
|
|
2011-01-08 21:15:00 +00:00
|
|
|
|
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
|
2017-05-10 12:48:57 +00:00
|
|
|
|
included that field's name in ``raw_id_fields``, ``radio_fields``, or
|
|
|
|
|
``autocomplete_fields``.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2011-01-08 21:15:00 +00:00
|
|
|
|
``formfield_overrides`` won't let you change the widget on relation
|
2017-05-10 12:48:57 +00:00
|
|
|
|
fields that have ``raw_id_fields``, ``radio_fields``, or
|
|
|
|
|
``autocomplete_fields`` set. That's because ``raw_id_fields``,
|
|
|
|
|
``radio_fields``, and ``autocomplete_fields`` imply custom widgets of
|
2011-01-08 21:15:00 +00:00
|
|
|
|
their own.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2011-01-08 21:15:00 +00:00
|
|
|
|
.. attribute:: ModelAdmin.inlines
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2012-10-19 00:12:41 +00:00
|
|
|
|
See :class:`InlineModelAdmin` objects below as well as
|
2013-09-04 01:01:45 +00:00
|
|
|
|
:meth:`ModelAdmin.get_formsets_with_inlines`.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2009-04-16 12:46:58 +00:00
|
|
|
|
.. attribute:: ModelAdmin.list_display
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
Set ``list_display`` to control which fields are displayed on the change
|
|
|
|
|
list page of the admin.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
Example::
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
list_display = ('first_name', 'last_name')
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
If you don't set ``list_display``, the admin site will display a single
|
2017-01-18 16:51:29 +00:00
|
|
|
|
column that displays the ``__str__()`` representation of each object.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
You have four possible values that can be used in ``list_display``:
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
* A field of the model. For example::
|
2009-02-22 06:08:13 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
class PersonAdmin(admin.ModelAdmin):
|
|
|
|
|
list_display = ('first_name', 'last_name')
|
2009-02-22 06:08:13 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
* A callable that accepts one parameter for the model instance. For
|
|
|
|
|
example::
|
2009-02-22 06:08:13 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
def upper_case_name(obj):
|
|
|
|
|
return ("%s %s" % (obj.first_name, obj.last_name)).upper()
|
|
|
|
|
upper_case_name.short_description = 'Name'
|
2009-02-22 06:08:13 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
class PersonAdmin(admin.ModelAdmin):
|
|
|
|
|
list_display = (upper_case_name,)
|
2009-02-22 06:08:13 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
* A string representing an attribute on the ``ModelAdmin``. This
|
|
|
|
|
behaves same as the callable. For example::
|
2009-02-22 06:08:13 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
class PersonAdmin(admin.ModelAdmin):
|
|
|
|
|
list_display = ('upper_case_name',)
|
2009-02-22 06:08:13 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
def upper_case_name(self, obj):
|
2013-05-21 11:03:45 +00:00
|
|
|
|
return ("%s %s" % (obj.first_name, obj.last_name)).upper()
|
2011-10-10 17:32:33 +00:00
|
|
|
|
upper_case_name.short_description = 'Name'
|
2009-02-22 06:08:13 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
* 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::
|
2008-07-27 18:36:08 +00:00
|
|
|
|
|
2013-05-19 10:42:35 +00:00
|
|
|
|
from django.contrib import admin
|
2018-05-12 17:37:42 +00:00
|
|
|
|
from django.db import models
|
2013-05-19 10:42:35 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
class Person(models.Model):
|
|
|
|
|
name = models.CharField(max_length=50)
|
|
|
|
|
birthday = models.DateField()
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
def decade_born_in(self):
|
|
|
|
|
return self.birthday.strftime('%Y')[:3] + "0's"
|
|
|
|
|
decade_born_in.short_description = 'Birth decade'
|
2008-07-27 18:36:08 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
class PersonAdmin(admin.ModelAdmin):
|
|
|
|
|
list_display = ('name', 'decade_born_in')
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
A few special cases to note about ``list_display``:
|
2008-08-14 20:12:19 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
* If the field is a ``ForeignKey``, Django will display the
|
2017-01-18 16:51:29 +00:00
|
|
|
|
``__str__()`` of the related object.
|
2008-08-14 20:12:19 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
* ``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``.)
|
2008-08-14 20:12:19 +00:00
|
|
|
|
|
2017-05-06 14:56:28 +00:00
|
|
|
|
* If the field is a ``BooleanField``, Django will display a pretty "on" or
|
|
|
|
|
"off" icon instead of ``True`` or ``False``.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
* If the string given is a method of the model, ``ModelAdmin`` or a
|
2015-09-08 19:46:26 +00:00
|
|
|
|
callable, Django will HTML-escape the output by default. To escape
|
|
|
|
|
user input and allow your own unescaped tags, use
|
|
|
|
|
:func:`~django.utils.html.format_html`.
|
2009-02-22 06:08:13 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
Here's a full example model::
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2013-05-19 10:42:35 +00:00
|
|
|
|
from django.contrib import admin
|
2018-05-12 17:37:42 +00:00
|
|
|
|
from django.db import models
|
2013-01-25 11:53:40 +00:00
|
|
|
|
from django.utils.html import format_html
|
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
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)
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
def colored_name(self):
|
2016-06-02 19:56:13 +00:00
|
|
|
|
return format_html(
|
|
|
|
|
'<span style="color: #{};">{} {}</span>',
|
|
|
|
|
self.color_code,
|
|
|
|
|
self.first_name,
|
|
|
|
|
self.last_name,
|
|
|
|
|
)
|
2013-01-25 11:53:40 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
class PersonAdmin(admin.ModelAdmin):
|
|
|
|
|
list_display = ('first_name', 'last_name', 'colored_name')
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2016-12-12 05:51:23 +00:00
|
|
|
|
* As some examples have already demonstrated, when using a callable, a
|
|
|
|
|
model method, or a ``ModelAdmin`` method, you can customize the column's
|
|
|
|
|
title by adding a ``short_description`` attribute to the callable.
|
|
|
|
|
|
2015-03-13 10:08:03 +00:00
|
|
|
|
* If the value of a field is ``None``, an empty string, or an iterable
|
|
|
|
|
without elements, Django will display ``-`` (a dash). You can override
|
|
|
|
|
this with :attr:`AdminSite.empty_value_display`::
|
|
|
|
|
|
|
|
|
|
from django.contrib import admin
|
|
|
|
|
|
|
|
|
|
admin.site.empty_value_display = '(None)'
|
|
|
|
|
|
2016-04-27 19:41:37 +00:00
|
|
|
|
You can also use :attr:`ModelAdmin.empty_value_display`::
|
2015-03-13 10:08:03 +00:00
|
|
|
|
|
|
|
|
|
class PersonAdmin(admin.ModelAdmin):
|
|
|
|
|
empty_value_display = 'unknown'
|
|
|
|
|
|
|
|
|
|
Or on a field level::
|
|
|
|
|
|
|
|
|
|
class PersonAdmin(admin.ModelAdmin):
|
|
|
|
|
list_display = ('name', 'birth_date_view')
|
|
|
|
|
|
|
|
|
|
def birth_date_view(self, obj):
|
|
|
|
|
return obj.birth_date
|
|
|
|
|
|
|
|
|
|
birth_date_view.empty_value_display = 'unknown'
|
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
* 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``.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
Here's a full example model::
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2013-05-19 10:42:35 +00:00
|
|
|
|
from django.contrib import admin
|
2018-05-12 17:37:42 +00:00
|
|
|
|
from django.db import models
|
2013-05-19 10:42:35 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
class Person(models.Model):
|
|
|
|
|
first_name = models.CharField(max_length=50)
|
|
|
|
|
birthday = models.DateField()
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
def born_in_fifties(self):
|
|
|
|
|
return self.birthday.strftime('%Y')[:3] == '195'
|
|
|
|
|
born_in_fifties.boolean = True
|
2008-07-27 18:36:08 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
class PersonAdmin(admin.ModelAdmin):
|
|
|
|
|
list_display = ('name', 'born_in_fifties')
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2017-01-18 16:51:29 +00:00
|
|
|
|
* The ``__str__()`` method is just as valid in ``list_display`` as any
|
|
|
|
|
other model method, so it's perfectly OK to do this::
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2014-02-09 11:38:13 +00:00
|
|
|
|
list_display = ('__str__', 'some_other_field')
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
* 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).
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
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.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
For example::
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2013-05-19 10:42:35 +00:00
|
|
|
|
from django.contrib import admin
|
2018-05-12 17:37:42 +00:00
|
|
|
|
from django.db import models
|
2013-01-25 11:53:40 +00:00
|
|
|
|
from django.utils.html import format_html
|
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
class Person(models.Model):
|
|
|
|
|
first_name = models.CharField(max_length=50)
|
|
|
|
|
color_code = models.CharField(max_length=6)
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
def colored_first_name(self):
|
2016-06-02 19:56:13 +00:00
|
|
|
|
return format_html(
|
|
|
|
|
'<span style="color: #{};">{}</span>',
|
|
|
|
|
self.color_code,
|
|
|
|
|
self.first_name,
|
|
|
|
|
)
|
2013-01-25 11:53:40 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
colored_first_name.admin_order_field = 'first_name'
|
2008-07-27 18:36:08 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
class PersonAdmin(admin.ModelAdmin):
|
|
|
|
|
list_display = ('first_name', 'colored_first_name')
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
The above will tell Django to order by the ``first_name`` field when
|
|
|
|
|
trying to sort by ``colored_first_name`` in the admin.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2015-01-20 16:44:41 +00:00
|
|
|
|
To indicate descending order with ``admin_order_field`` you can use a
|
2014-01-23 02:13:59 +00:00
|
|
|
|
hyphen prefix on the field name. Using the above example, this would
|
|
|
|
|
look like::
|
|
|
|
|
|
|
|
|
|
colored_first_name.admin_order_field = '-first_name'
|
|
|
|
|
|
2015-11-07 20:50:40 +00:00
|
|
|
|
``admin_order_field`` supports query lookups to sort by values on related
|
|
|
|
|
models. This example includes an "author first name" column in the list
|
|
|
|
|
display and allows sorting it by first name::
|
|
|
|
|
|
|
|
|
|
class Blog(models.Model):
|
|
|
|
|
title = models.CharField(max_length=255)
|
|
|
|
|
author = models.ForeignKey(Person, on_delete=models.CASCADE)
|
|
|
|
|
|
|
|
|
|
class BlogAdmin(admin.ModelAdmin):
|
|
|
|
|
list_display = ('title', 'author', 'author_first_name')
|
|
|
|
|
|
|
|
|
|
def author_first_name(self, obj):
|
|
|
|
|
return obj.author.first_name
|
|
|
|
|
|
|
|
|
|
author_first_name.admin_order_field = 'author__first_name'
|
|
|
|
|
|
2018-02-16 02:00:31 +00:00
|
|
|
|
:doc:`Query expressions </ref/models/expressions>` may be used in
|
|
|
|
|
``admin_order_field``. For example::
|
|
|
|
|
|
|
|
|
|
from django.db.models import Value
|
|
|
|
|
from django.db.models.functions import Concat
|
|
|
|
|
|
|
|
|
|
class Person(models.Model):
|
|
|
|
|
first_name = models.CharField(max_length=50)
|
|
|
|
|
last_name = models.CharField(max_length=50)
|
|
|
|
|
|
|
|
|
|
def full_name(self):
|
|
|
|
|
return self.first_name + ' ' + self.last_name
|
|
|
|
|
full_name.admin_order_field = Concat('first_name', Value(' '), 'last_name')
|
|
|
|
|
|
|
|
|
|
.. versionadded:: 2.1
|
|
|
|
|
|
|
|
|
|
Support for expressions in ``admin_order_field`` was added.
|
|
|
|
|
|
2013-05-21 11:03:45 +00:00
|
|
|
|
* Elements of ``list_display`` can also be properties. Please note however,
|
|
|
|
|
that due to the way properties work in Python, setting
|
|
|
|
|
``short_description`` on a property is only possible when using the
|
|
|
|
|
``property()`` function and **not** with the ``@property`` decorator.
|
|
|
|
|
|
|
|
|
|
For example::
|
|
|
|
|
|
2015-01-13 16:28:43 +00:00
|
|
|
|
class Person(models.Model):
|
2013-05-21 11:03:45 +00:00
|
|
|
|
first_name = models.CharField(max_length=50)
|
|
|
|
|
last_name = models.CharField(max_length=50)
|
|
|
|
|
|
|
|
|
|
def my_property(self):
|
|
|
|
|
return self.first_name + ' ' + self.last_name
|
|
|
|
|
my_property.short_description = "Full name of the person"
|
|
|
|
|
|
|
|
|
|
full_name = property(my_property)
|
|
|
|
|
|
|
|
|
|
class PersonAdmin(admin.ModelAdmin):
|
|
|
|
|
list_display = ('full_name',)
|
|
|
|
|
|
|
|
|
|
|
2014-03-24 15:42:56 +00:00
|
|
|
|
* The field names in ``list_display`` will also appear as CSS classes in
|
2013-02-24 16:31:26 +00:00
|
|
|
|
the HTML output, in the form of ``column-<field_name>`` on each ``<th>``
|
|
|
|
|
element. This can be used to set column widths in a CSS file for example.
|
|
|
|
|
|
2014-02-22 14:18:51 +00:00
|
|
|
|
* Django will try to interpret every element of ``list_display`` in this
|
|
|
|
|
order:
|
|
|
|
|
|
|
|
|
|
* A field of the model.
|
|
|
|
|
* A callable.
|
|
|
|
|
* A string representing a ``ModelAdmin`` attribute.
|
|
|
|
|
* A string representing a model attribute.
|
|
|
|
|
|
|
|
|
|
For example if you have ``first_name`` as a model field and
|
|
|
|
|
as a ``ModelAdmin`` attribute, the model field will be used.
|
|
|
|
|
|
2013-02-24 16:31:26 +00:00
|
|
|
|
|
2009-04-16 12:46:58 +00:00
|
|
|
|
.. attribute:: ModelAdmin.list_display_links
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2013-09-06 18:25:13 +00:00
|
|
|
|
Use ``list_display_links`` to control if and which fields in
|
|
|
|
|
:attr:`list_display` should be linked to the "change" page for an object.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
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.
|
2013-09-06 18:25:13 +00:00
|
|
|
|
But ``list_display_links`` lets you change this:
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2013-09-06 18:25:13 +00:00
|
|
|
|
* Set it to ``None`` to get no links at all.
|
|
|
|
|
* Set it to a list or tuple of fields (in the same format as
|
|
|
|
|
``list_display``) whose columns you want converted to links.
|
|
|
|
|
|
|
|
|
|
You 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 that if you want to use
|
|
|
|
|
``list_display_links`` in this fashion, you must define ``list_display``.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
In this example, the ``first_name`` and ``last_name`` fields will be
|
|
|
|
|
linked on the change list page::
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
class PersonAdmin(admin.ModelAdmin):
|
|
|
|
|
list_display = ('first_name', 'last_name', 'birthday')
|
|
|
|
|
list_display_links = ('first_name', 'last_name')
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2013-09-06 18:25:13 +00:00
|
|
|
|
In this example, the change list page grid will have no links::
|
|
|
|
|
|
|
|
|
|
class AuditEntryAdmin(admin.ModelAdmin):
|
|
|
|
|
list_display = ('timestamp', 'message')
|
|
|
|
|
list_display_links = None
|
|
|
|
|
|
|
|
|
|
.. _admin-list-editable:
|
2009-03-23 23:25:03 +00:00
|
|
|
|
|
2009-04-16 12:46:58 +00:00
|
|
|
|
.. attribute:: ModelAdmin.list_editable
|
2009-03-17 20:51:47 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
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.
|
2009-03-17 20:51:47 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
.. note::
|
2009-03-17 20:51:47 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
``list_editable`` interacts with a couple of other options in
|
|
|
|
|
particular ways; you should note the following rules:
|
2009-03-24 11:41:37 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
* Any field in ``list_editable`` must also be in ``list_display``.
|
|
|
|
|
You can't edit a field that's not displayed!
|
2009-03-24 11:41:37 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
* 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.
|
2009-03-24 11:41:37 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
You'll get a validation error if either of these rules are broken.
|
2009-03-17 20:51:47 +00:00
|
|
|
|
|
2009-04-16 12:46:58 +00:00
|
|
|
|
.. attribute:: ModelAdmin.list_filter
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2011-05-03 10:44:23 +00:00
|
|
|
|
Set ``list_filter`` to activate filters in the right sidebar of the change
|
|
|
|
|
list page of the admin, as illustrated in the following screenshot:
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2015-09-11 03:07:10 +00:00
|
|
|
|
.. image:: _images/list_filter.png
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2012-11-25 19:39:23 +00:00
|
|
|
|
``list_filter`` should be a list or tuple of elements, where each element
|
|
|
|
|
should be of one of the following types:
|
2011-05-03 10:44:23 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
* a field name, where the specified field should be either a
|
|
|
|
|
``BooleanField``, ``CharField``, ``DateField``, ``DateTimeField``,
|
|
|
|
|
``IntegerField``, ``ForeignKey`` or ``ManyToManyField``, for example::
|
|
|
|
|
|
2013-05-19 10:42:35 +00:00
|
|
|
|
class PersonAdmin(admin.ModelAdmin):
|
2011-10-10 17:32:33 +00:00
|
|
|
|
list_filter = ('is_staff', 'company')
|
|
|
|
|
|
|
|
|
|
Field names in ``list_filter`` can also span relations
|
|
|
|
|
using the ``__`` lookup, for example::
|
|
|
|
|
|
2013-05-19 10:42:35 +00:00
|
|
|
|
class PersonAdmin(admin.UserAdmin):
|
2011-10-10 17:32:33 +00:00
|
|
|
|
list_filter = ('company__name',)
|
|
|
|
|
|
2013-01-01 13:12:42 +00:00
|
|
|
|
* a class inheriting from ``django.contrib.admin.SimpleListFilter``,
|
2011-10-10 17:32:33 +00:00
|
|
|
|
which you need to provide the ``title`` and ``parameter_name``
|
|
|
|
|
attributes to and override the ``lookups`` and ``queryset`` methods,
|
|
|
|
|
e.g.::
|
|
|
|
|
|
2012-03-21 05:57:22 +00:00
|
|
|
|
from datetime import date
|
|
|
|
|
|
2013-05-19 10:42:35 +00:00
|
|
|
|
from django.contrib import admin
|
2017-01-26 19:58:33 +00:00
|
|
|
|
from django.utils.translation import gettext_lazy as _
|
2011-10-10 17:32:33 +00:00
|
|
|
|
|
2013-05-19 10:42:35 +00:00
|
|
|
|
class DecadeBornListFilter(admin.SimpleListFilter):
|
2011-10-10 17:32:33 +00:00
|
|
|
|
# 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()`.
|
|
|
|
|
"""
|
2012-08-15 17:41:16 +00:00
|
|
|
|
# Compare the requested value (either '80s' or '90s')
|
2011-10-10 17:32:33 +00:00
|
|
|
|
# to decide how to filter the queryset.
|
|
|
|
|
if self.value() == '80s':
|
2012-03-21 05:57:22 +00:00
|
|
|
|
return queryset.filter(birthday__gte=date(1980, 1, 1),
|
|
|
|
|
birthday__lte=date(1989, 12, 31))
|
2011-10-10 17:32:33 +00:00
|
|
|
|
if self.value() == '90s':
|
2012-03-21 05:57:22 +00:00
|
|
|
|
return queryset.filter(birthday__gte=date(1990, 1, 1),
|
|
|
|
|
birthday__lte=date(1999, 12, 31))
|
2011-10-10 17:32:33 +00:00
|
|
|
|
|
2013-05-19 10:42:35 +00:00
|
|
|
|
class PersonAdmin(admin.ModelAdmin):
|
2011-10-10 17:32:33 +00:00
|
|
|
|
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:
|
2017-01-22 06:57:14 +00:00
|
|
|
|
return super().lookups(request, model_admin)
|
2011-10-10 17:32:33 +00:00
|
|
|
|
|
|
|
|
|
def queryset(self, request, queryset):
|
|
|
|
|
if request.user.is_superuser:
|
2017-01-22 06:57:14 +00:00
|
|
|
|
return super().queryset(request, queryset)
|
2011-10-10 17:32:33 +00:00
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
"""
|
2013-03-08 14:15:23 +00:00
|
|
|
|
qs = model_admin.get_queryset(request)
|
2012-03-21 05:57:22 +00:00
|
|
|
|
if qs.filter(birthday__gte=date(1980, 1, 1),
|
|
|
|
|
birthday__lte=date(1989, 12, 31)).exists():
|
2011-10-10 17:32:33 +00:00
|
|
|
|
yield ('80s', _('in the eighties'))
|
2012-03-21 05:57:22 +00:00
|
|
|
|
if qs.filter(birthday__gte=date(1990, 1, 1),
|
|
|
|
|
birthday__lte=date(1999, 12, 31)).exists():
|
2011-10-10 17:32:33 +00:00
|
|
|
|
yield ('90s', _('in the nineties'))
|
|
|
|
|
|
|
|
|
|
* a tuple, where the first element is a field name and the second
|
|
|
|
|
element is a class inheriting from
|
2013-01-01 13:12:42 +00:00
|
|
|
|
``django.contrib.admin.FieldListFilter``, for example::
|
2011-10-10 17:32:33 +00:00
|
|
|
|
|
2013-05-19 10:42:35 +00:00
|
|
|
|
class PersonAdmin(admin.ModelAdmin):
|
2011-10-10 17:32:33 +00:00
|
|
|
|
list_filter = (
|
2013-05-19 10:42:35 +00:00
|
|
|
|
('is_staff', admin.BooleanFieldListFilter),
|
2011-10-10 17:32:33 +00:00
|
|
|
|
)
|
|
|
|
|
|
2015-10-26 13:03:04 +00:00
|
|
|
|
You can limit the choices of a related model to the objects involved in
|
|
|
|
|
that relation using ``RelatedOnlyFieldListFilter``::
|
2014-05-21 10:32:19 +00:00
|
|
|
|
|
|
|
|
|
class BookAdmin(admin.ModelAdmin):
|
|
|
|
|
list_filter = (
|
|
|
|
|
('author', admin.RelatedOnlyFieldListFilter),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
Assuming ``author`` is a ``ForeignKey`` to a ``User`` model, this will
|
|
|
|
|
limit the ``list_filter`` choices to the users who have written a book
|
|
|
|
|
instead of listing all users.
|
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
.. note::
|
|
|
|
|
|
2012-02-17 20:04:11 +00:00
|
|
|
|
The ``FieldListFilter`` API is considered internal and might be
|
|
|
|
|
changed.
|
2012-02-09 18:59:05 +00:00
|
|
|
|
|
2016-09-02 01:24:34 +00:00
|
|
|
|
List filter's typically appear only if the filter has more than one choice.
|
|
|
|
|
A filter's ``has_output()`` method controls whether or not it appears.
|
|
|
|
|
|
2012-02-09 18:59:05 +00:00
|
|
|
|
It is possible to specify a custom template for rendering a list filter::
|
|
|
|
|
|
2013-05-19 10:42:35 +00:00
|
|
|
|
class FilterWithCustomTemplate(admin.SimpleListFilter):
|
2012-02-09 18:59:05 +00:00
|
|
|
|
template = "custom_template.html"
|
|
|
|
|
|
2016-11-15 22:00:50 +00:00
|
|
|
|
See the default template provided by Django (``admin/filter.html``) for
|
2012-02-09 18:59:05 +00:00
|
|
|
|
a concrete example.
|
2011-04-03 23:08:48 +00:00
|
|
|
|
|
2011-09-08 13:25:00 +00:00
|
|
|
|
.. attribute:: ModelAdmin.list_max_show_all
|
|
|
|
|
|
|
|
|
|
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``.
|
|
|
|
|
|
2009-04-16 12:46:58 +00:00
|
|
|
|
.. attribute:: ModelAdmin.list_per_page
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
Set ``list_per_page`` to control how many items appear on each paginated
|
|
|
|
|
admin change list page. By default, this is set to ``100``.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2009-04-16 12:46:58 +00:00
|
|
|
|
.. attribute:: ModelAdmin.list_select_related
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
Set ``list_select_related`` to tell Django to use
|
2011-09-30 10:28:39 +00:00
|
|
|
|
: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.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2013-06-04 21:35:11 +00:00
|
|
|
|
The value should be either a boolean, a list or a tuple. Default is
|
|
|
|
|
``False``.
|
|
|
|
|
|
|
|
|
|
When value is ``True``, ``select_related()`` will always be called. When
|
|
|
|
|
value is set to ``False``, Django will look at ``list_display`` and call
|
|
|
|
|
``select_related()`` if any ``ForeignKey`` is present.
|
|
|
|
|
|
|
|
|
|
If you need more fine-grained control, use a tuple (or list) as value for
|
|
|
|
|
``list_select_related``. Empty tuple will prevent Django from calling
|
|
|
|
|
``select_related`` at all. Any other tuple will be passed directly to
|
|
|
|
|
``select_related`` as parameters. For example::
|
|
|
|
|
|
|
|
|
|
class ArticleAdmin(admin.ModelAdmin):
|
|
|
|
|
list_select_related = ('author', 'category')
|
|
|
|
|
|
|
|
|
|
will call ``select_related('author', 'category')``.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2015-03-16 10:00:16 +00:00
|
|
|
|
If you need to specify a dynamic value based on the request, you can
|
|
|
|
|
implement a :meth:`~ModelAdmin.get_list_select_related` method.
|
|
|
|
|
|
2009-04-16 12:46:58 +00:00
|
|
|
|
.. attribute:: ModelAdmin.ordering
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-12 22:56:01 +00:00
|
|
|
|
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
|
2011-01-08 21:15:00 +00:00
|
|
|
|
model's :attr:`~django.db.models.Options.ordering` parameter.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
If this isn't provided, the Django admin will use the model's default
|
|
|
|
|
ordering.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2011-06-12 13:04:53 +00:00
|
|
|
|
If you need to specify a dynamic order (for example depending on user or
|
|
|
|
|
language) you can implement a :meth:`~ModelAdmin.get_ordering` method.
|
|
|
|
|
|
2010-12-21 14:57:29 +00:00
|
|
|
|
.. attribute:: ModelAdmin.paginator
|
|
|
|
|
|
|
|
|
|
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
|
2010-12-21 19:19:04 +00:00
|
|
|
|
provide an implementation for :meth:`ModelAdmin.get_paginator`.
|
2010-12-21 14:57:29 +00:00
|
|
|
|
|
2009-04-16 12:46:58 +00:00
|
|
|
|
.. attribute:: ModelAdmin.prepopulated_fields
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
Set ``prepopulated_fields`` to a dictionary mapping field names to the
|
|
|
|
|
fields it should prepopulate from::
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
class ArticleAdmin(admin.ModelAdmin):
|
|
|
|
|
prepopulated_fields = {"slug": ("title",)}
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
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
|
2018-04-28 23:14:09 +00:00
|
|
|
|
slug (e.g. substituting dashes for spaces; lowercasing ASCII letters; and
|
|
|
|
|
removing various English stop words such as 'a', 'an', 'as', and similar).
|
2008-07-27 18:27:56 +00:00
|
|
|
|
|
2017-10-27 15:23:40 +00:00
|
|
|
|
Fields are prepopulated on add forms but not on change forms. It's usually
|
|
|
|
|
undesired that slugs change after an object is created (which would cause
|
|
|
|
|
an object's URL to change if the slug is used in it).
|
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
``prepopulated_fields`` doesn't accept ``DateTimeField``, ``ForeignKey``,
|
2016-12-19 13:33:46 +00:00
|
|
|
|
``OneToOneField``, and ``ManyToManyField`` fields.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2013-06-18 19:41:36 +00:00
|
|
|
|
.. attribute:: ModelAdmin.preserve_filters
|
|
|
|
|
|
|
|
|
|
The admin now preserves filters on the list view after creating, editing
|
|
|
|
|
or deleting an object. You can restore the previous behavior of clearing
|
|
|
|
|
filters by setting this attribute to ``False``.
|
|
|
|
|
|
2009-04-16 12:46:58 +00:00
|
|
|
|
.. attribute:: ModelAdmin.radio_fields
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
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::
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
class PersonAdmin(admin.ModelAdmin):
|
|
|
|
|
radio_fields = {"group": admin.VERTICAL}
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
You have the choice of using ``HORIZONTAL`` or ``VERTICAL`` from the
|
|
|
|
|
``django.contrib.admin`` module.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
Don't include a field in ``radio_fields`` unless it's a ``ForeignKey`` or has
|
|
|
|
|
``choices`` set.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2017-05-10 12:48:57 +00:00
|
|
|
|
.. attribute:: ModelAdmin.autocomplete_fields
|
|
|
|
|
|
|
|
|
|
``autocomplete_fields`` is a list of ``ForeignKey`` and/or
|
|
|
|
|
``ManyToManyField`` fields you would like to change to `Select2
|
|
|
|
|
<https://select2.org/>`_ autocomplete inputs.
|
|
|
|
|
|
2017-09-19 09:07:34 +00:00
|
|
|
|
By default, the admin uses a select-box interface (``<select>``) for
|
|
|
|
|
those fields. Sometimes you don't want to incur the overhead of selecting
|
|
|
|
|
all the related instances to display in the dropdown.
|
2017-05-10 12:48:57 +00:00
|
|
|
|
|
|
|
|
|
The Select2 input looks similar to the default input but comes with a
|
|
|
|
|
search feature that loads the options asynchronously. This is faster and
|
|
|
|
|
more user-friendly if the related model has many instances.
|
|
|
|
|
|
|
|
|
|
You must define :attr:`~ModelAdmin.search_fields` on the related object's
|
|
|
|
|
``ModelAdmin`` because the autocomplete search uses it.
|
|
|
|
|
|
|
|
|
|
Ordering and pagination of the results are controlled by the related
|
|
|
|
|
``ModelAdmin``'s :meth:`~ModelAdmin.get_ordering` and
|
|
|
|
|
:meth:`~ModelAdmin.get_paginator` methods.
|
|
|
|
|
|
|
|
|
|
In the following example, ``ChoiceAdmin`` has an autocomplete field for the
|
|
|
|
|
``ForeignKey`` to the ``Question``. The results are filtered by the
|
|
|
|
|
``question_text`` field and ordered by the ``date_created`` field::
|
|
|
|
|
|
|
|
|
|
class QuestionAdmin(admin.ModelAdmin):
|
|
|
|
|
ordering = ['date_created']
|
|
|
|
|
search_fields = ['question_text']
|
|
|
|
|
|
|
|
|
|
class ChoiceAdmin(admin.ModelAdmin):
|
|
|
|
|
autocomplete_fields = ['question']
|
|
|
|
|
|
|
|
|
|
.. admonition:: Performance considerations for large datasets
|
|
|
|
|
|
|
|
|
|
Ordering using :attr:`ModelAdmin.ordering` may cause performance
|
|
|
|
|
problems as sorting on a large queryset will be slow.
|
|
|
|
|
|
|
|
|
|
Also, if your search fields include fields that aren't indexed by the
|
|
|
|
|
database, you might encounter poor performance on extremely large
|
|
|
|
|
tables.
|
|
|
|
|
|
|
|
|
|
For those cases, it's a good idea to write your own
|
|
|
|
|
:func:`ModelAdmin.get_search_results` implementation using a
|
|
|
|
|
full-text indexed search.
|
|
|
|
|
|
|
|
|
|
You may also want to change the ``Paginator`` on very large tables
|
|
|
|
|
as the default paginator always performs a ``count()`` query.
|
|
|
|
|
For example, you could override the default implementation of the
|
|
|
|
|
``Paginator.count`` property.
|
|
|
|
|
|
2009-04-16 12:46:58 +00:00
|
|
|
|
.. attribute:: ModelAdmin.raw_id_fields
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
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.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
``raw_id_fields`` is a list of fields you would like to change
|
|
|
|
|
into an ``Input`` widget for either a ``ForeignKey`` or
|
|
|
|
|
``ManyToManyField``::
|
2008-08-01 19:47:26 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
class ArticleAdmin(admin.ModelAdmin):
|
|
|
|
|
raw_id_fields = ("newspaper",)
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2012-10-24 20:30:23 +00:00
|
|
|
|
The ``raw_id_fields`` ``Input`` widget should contain a primary key if the
|
|
|
|
|
field is a ``ForeignKey`` or a comma separated list of values if the field
|
|
|
|
|
is a ``ManyToManyField``. The ``raw_id_fields`` widget shows a magnifying
|
|
|
|
|
glass button next to the field which allows users to search for and select
|
|
|
|
|
a value:
|
|
|
|
|
|
|
|
|
|
.. image:: _images/raw_id_fields.png
|
|
|
|
|
|
2009-12-22 18:29:00 +00:00
|
|
|
|
.. attribute:: ModelAdmin.readonly_fields
|
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
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
|
2012-11-06 11:58:25 +00:00
|
|
|
|
as-is and non-editable; they are also excluded from the
|
|
|
|
|
:class:`~django.forms.ModelForm` used for creating and editing. Note that
|
|
|
|
|
when specifying :attr:`ModelAdmin.fields` or :attr:`ModelAdmin.fieldsets`
|
|
|
|
|
the read-only fields must be present to be shown (they are ignored
|
|
|
|
|
otherwise).
|
2009-12-22 18:29:00 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
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.
|
2009-12-22 18:29:00 +00:00
|
|
|
|
|
2012-11-02 20:48:55 +00:00
|
|
|
|
A read-only field can not only display data from a model's field, it can
|
2013-01-29 15:45:40 +00:00
|
|
|
|
also display the output of a model's method or a method of the
|
2012-11-02 20:48:55 +00:00
|
|
|
|
``ModelAdmin`` class itself. This is very similar to the way
|
|
|
|
|
:attr:`ModelAdmin.list_display` behaves. This provides an easy way to use
|
|
|
|
|
the admin interface to provide feedback on the status of the objects being
|
|
|
|
|
edited, for example::
|
|
|
|
|
|
2013-05-19 10:42:35 +00:00
|
|
|
|
from django.contrib import admin
|
2013-01-25 11:53:40 +00:00
|
|
|
|
from django.utils.html import format_html_join
|
|
|
|
|
from django.utils.safestring import mark_safe
|
|
|
|
|
|
2013-05-19 10:42:35 +00:00
|
|
|
|
class PersonAdmin(admin.ModelAdmin):
|
2012-11-02 20:48:55 +00:00
|
|
|
|
readonly_fields = ('address_report',)
|
|
|
|
|
|
|
|
|
|
def address_report(self, instance):
|
2013-01-25 11:53:40 +00:00
|
|
|
|
# assuming get_full_address() returns a list of strings
|
|
|
|
|
# for each line of the address and you want to separate each
|
|
|
|
|
# line by a linebreak
|
|
|
|
|
return format_html_join(
|
2018-01-21 07:09:10 +00:00
|
|
|
|
mark_safe('<br>'),
|
2014-11-27 00:41:27 +00:00
|
|
|
|
'{}',
|
2013-01-25 11:53:40 +00:00
|
|
|
|
((line,) for line in instance.get_full_address()),
|
2015-09-08 19:46:26 +00:00
|
|
|
|
) or mark_safe("<span class='errors'>I can't determine this address.</span>")
|
2012-11-02 20:48:55 +00:00
|
|
|
|
|
|
|
|
|
# short_description functions like a model field's verbose_name
|
|
|
|
|
address_report.short_description = "Address"
|
|
|
|
|
|
2009-04-16 12:46:58 +00:00
|
|
|
|
.. attribute:: ModelAdmin.save_as
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2016-04-03 11:03:44 +00:00
|
|
|
|
Set ``save_as`` to enable a "save as new" feature on admin change forms.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
Normally, objects have three save options: "Save", "Save and continue
|
2016-04-03 11:03:44 +00:00
|
|
|
|
editing", and "Save and add another". If ``save_as`` is ``True``, "Save
|
|
|
|
|
and add another" will be replaced by a "Save as new" button that creates a
|
|
|
|
|
new object (with a new ID) rather than updating the existing object.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
By default, ``save_as`` is set to ``False``.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2016-04-03 14:50:01 +00:00
|
|
|
|
.. attribute:: ModelAdmin.save_as_continue
|
|
|
|
|
|
|
|
|
|
When :attr:`save_as=True <save_as>`, the default redirect after saving the
|
|
|
|
|
new object is to the change view for that object. If you set
|
|
|
|
|
``save_as_continue=False``, the redirect will be to the changelist view.
|
|
|
|
|
|
|
|
|
|
By default, ``save_as_continue`` is set to ``True``.
|
|
|
|
|
|
2009-04-16 12:46:58 +00:00
|
|
|
|
.. attribute:: ModelAdmin.save_on_top
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
Set ``save_on_top`` to add save buttons across the top of your admin change
|
|
|
|
|
forms.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
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.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
By default, ``save_on_top`` is set to ``False``.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2009-04-16 12:46:58 +00:00
|
|
|
|
.. attribute:: ModelAdmin.search_fields
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
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.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
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::
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
search_fields = ['foreign_key__related_fieldname']
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
For example, if you have a blog entry with an author, the following
|
2015-05-21 15:07:38 +00:00
|
|
|
|
definition would enable searching blog entries by the email address of the
|
2010-12-05 04:52:31 +00:00
|
|
|
|
author::
|
2010-08-07 07:57:15 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
search_fields = ['user__email']
|
2010-08-07 07:57:15 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
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
|
2017-07-05 11:00:10 +00:00
|
|
|
|
words, case-insensitive (using the :lookup:`icontains` lookup), 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::
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
WHERE (first_name ILIKE '%john%' OR last_name ILIKE '%john%')
|
|
|
|
|
AND (first_name ILIKE '%lennon%' OR last_name ILIKE '%lennon%')
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2017-07-05 11:00:10 +00:00
|
|
|
|
If you don't want to use ``icontains`` as the lookup, you can use any
|
|
|
|
|
lookup by appending it the field. For example, you could use :lookup:`exact`
|
|
|
|
|
by setting ``search_fields`` to ``['first_name__exact']``.
|
|
|
|
|
|
|
|
|
|
Beware that because query terms are split and ANDed as described earlier,
|
|
|
|
|
searching with :lookup:`exact` only works with a single search word since
|
|
|
|
|
two or more words can't all be an exact match unless all words are the same.
|
|
|
|
|
|
|
|
|
|
.. versionadded:: 2.1
|
|
|
|
|
|
|
|
|
|
The ability to specify a field lookup was added.
|
|
|
|
|
|
|
|
|
|
Some (older) shortcuts for specifying a field lookup are also available.
|
|
|
|
|
You can prefix a field in ``search_fields`` with the following characters
|
|
|
|
|
and it's equivalent to adding ``__<lookup>`` to the field:
|
|
|
|
|
|
|
|
|
|
====== ====================
|
|
|
|
|
Prefix Lookup
|
|
|
|
|
====== ====================
|
|
|
|
|
^ :lookup:`startswith`
|
|
|
|
|
= :lookup:`iexact`
|
|
|
|
|
@ :lookup:`search`
|
|
|
|
|
None :lookup:`icontains`
|
|
|
|
|
====== ====================
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2013-06-05 16:55:50 +00:00
|
|
|
|
If you need to customize search you can use
|
|
|
|
|
:meth:`ModelAdmin.get_search_results` to provide additional or alternate
|
|
|
|
|
search behavior.
|
2013-05-19 08:52:29 +00:00
|
|
|
|
|
2014-09-22 16:46:43 +00:00
|
|
|
|
.. attribute:: ModelAdmin.show_full_result_count
|
|
|
|
|
|
|
|
|
|
Set ``show_full_result_count`` to control whether the full count of objects
|
|
|
|
|
should be displayed on a filtered admin page (e.g. ``99 results (103 total)``).
|
|
|
|
|
If this option is set to ``False``, a text like ``99 results (Show all)``
|
|
|
|
|
is displayed instead.
|
|
|
|
|
|
|
|
|
|
The default of ``show_full_result_count=True`` generates a query to perform
|
|
|
|
|
a full count on the table which can be expensive if the table contains a
|
|
|
|
|
large number of rows.
|
|
|
|
|
|
2016-02-09 00:35:03 +00:00
|
|
|
|
.. attribute:: ModelAdmin.sortable_by
|
|
|
|
|
|
|
|
|
|
.. versionadded:: 2.1
|
|
|
|
|
|
|
|
|
|
By default, the change list page allows sorting by all model fields (and
|
|
|
|
|
callables that have the ``admin_order_field`` property) specified in
|
|
|
|
|
:attr:`list_display`.
|
|
|
|
|
|
|
|
|
|
If you want to disable sorting for some columns, set ``sortable_by`` to
|
|
|
|
|
a collection (e.g. ``list``, ``tuple``, or ``set``) of the subset of
|
|
|
|
|
:attr:`list_display` that you want to be sortable. An empty collection
|
|
|
|
|
disables sorting for all columns.
|
|
|
|
|
|
|
|
|
|
If you need to specify this list dynamically, implement a
|
|
|
|
|
:meth:`~ModelAdmin.get_sortable_by` method instead.
|
|
|
|
|
|
2013-10-24 15:28:09 +00:00
|
|
|
|
.. attribute:: ModelAdmin.view_on_site
|
|
|
|
|
|
|
|
|
|
Set ``view_on_site`` to control whether or not to display the "View on site" link.
|
|
|
|
|
This link should bring you to a URL where you can display the saved object.
|
|
|
|
|
|
|
|
|
|
This value can be either a boolean flag or a callable. If ``True`` (the
|
|
|
|
|
default), the object's :meth:`~django.db.models.Model.get_absolute_url`
|
|
|
|
|
method will be used to generate the url.
|
|
|
|
|
|
|
|
|
|
If your model has a :meth:`~django.db.models.Model.get_absolute_url` method
|
|
|
|
|
but you don't want the "View on site" button to appear, you only need to set
|
|
|
|
|
``view_on_site`` to ``False``::
|
|
|
|
|
|
|
|
|
|
from django.contrib import admin
|
|
|
|
|
|
|
|
|
|
class PersonAdmin(admin.ModelAdmin):
|
|
|
|
|
view_on_site = False
|
|
|
|
|
|
|
|
|
|
In case it is a callable, it accepts the model instance as a parameter.
|
|
|
|
|
For example::
|
|
|
|
|
|
|
|
|
|
from django.contrib import admin
|
2015-12-30 15:51:16 +00:00
|
|
|
|
from django.urls import reverse
|
2013-10-24 15:28:09 +00:00
|
|
|
|
|
|
|
|
|
class PersonAdmin(admin.ModelAdmin):
|
|
|
|
|
def view_on_site(self, obj):
|
2016-06-02 19:56:13 +00:00
|
|
|
|
url = reverse('person-detail', kwargs={'slug': obj.slug})
|
|
|
|
|
return 'https://example.com' + url
|
2013-10-24 15:28:09 +00:00
|
|
|
|
|
2010-03-27 21:12:56 +00:00
|
|
|
|
Custom template options
|
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~
|
2009-06-24 14:02:22 +00:00
|
|
|
|
|
2014-10-06 12:59:04 +00:00
|
|
|
|
The :ref:`admin-overriding-templates` section describes how to override or extend
|
2010-03-27 21:12:56 +00:00
|
|
|
|
the default admin templates. Use the following options to override the default
|
|
|
|
|
templates used by the :class:`ModelAdmin` views:
|
2009-06-24 14:02:22 +00:00
|
|
|
|
|
2010-01-12 23:34:03 +00:00
|
|
|
|
.. attribute:: ModelAdmin.add_form_template
|
|
|
|
|
|
2010-03-27 21:12:56 +00:00
|
|
|
|
Path to a custom template, used by :meth:`add_view`.
|
2010-01-12 23:34:03 +00:00
|
|
|
|
|
2009-06-24 14:02:22 +00:00
|
|
|
|
.. attribute:: ModelAdmin.change_form_template
|
|
|
|
|
|
2010-03-27 21:12:56 +00:00
|
|
|
|
Path to a custom template, used by :meth:`change_view`.
|
2009-06-24 14:02:22 +00:00
|
|
|
|
|
2010-03-27 21:12:56 +00:00
|
|
|
|
.. attribute:: ModelAdmin.change_list_template
|
2009-06-24 14:02:22 +00:00
|
|
|
|
|
2010-03-27 21:12:56 +00:00
|
|
|
|
Path to a custom template, used by :meth:`changelist_view`.
|
2009-06-24 14:02:22 +00:00
|
|
|
|
|
2010-03-27 21:12:56 +00:00
|
|
|
|
.. attribute:: ModelAdmin.delete_confirmation_template
|
2009-06-24 14:02:22 +00:00
|
|
|
|
|
2010-03-27 21:12:56 +00:00
|
|
|
|
Path to a custom template, used by :meth:`delete_view` for displaying a
|
|
|
|
|
confirmation page when deleting one or more objects.
|
2009-06-24 14:02:22 +00:00
|
|
|
|
|
2010-04-05 12:02:27 +00:00
|
|
|
|
.. attribute:: ModelAdmin.delete_selected_confirmation_template
|
|
|
|
|
|
2013-01-01 13:12:42 +00:00
|
|
|
|
Path to a custom template, used by the ``delete_selected`` action method
|
|
|
|
|
for displaying a confirmation page when deleting one or more objects. See
|
|
|
|
|
the :doc:`actions documentation</ref/contrib/admin/actions>`.
|
2010-04-05 12:02:27 +00:00
|
|
|
|
|
2010-03-27 21:12:56 +00:00
|
|
|
|
.. attribute:: ModelAdmin.object_history_template
|
2009-06-24 14:02:22 +00:00
|
|
|
|
|
2010-03-27 21:12:56 +00:00
|
|
|
|
Path to a custom template, used by :meth:`history_view`.
|
2009-06-24 14:02:22 +00:00
|
|
|
|
|
2016-11-12 19:09:15 +00:00
|
|
|
|
.. attribute:: ModelAdmin.popup_response_template
|
|
|
|
|
|
|
|
|
|
Path to a custom template, used by :meth:`response_add`,
|
|
|
|
|
:meth:`response_change`, and :meth:`response_delete`.
|
2009-06-24 14:02:22 +00:00
|
|
|
|
|
2009-07-03 14:09:04 +00:00
|
|
|
|
.. _model-admin-methods:
|
|
|
|
|
|
2008-08-09 20:52:40 +00:00
|
|
|
|
``ModelAdmin`` methods
|
|
|
|
|
----------------------
|
|
|
|
|
|
2010-11-21 19:00:40 +00:00
|
|
|
|
.. warning::
|
|
|
|
|
|
2016-12-02 18:52:24 +00:00
|
|
|
|
When overriding :meth:`ModelAdmin.save_model` and
|
|
|
|
|
:meth:`ModelAdmin.delete_model`, your code must save/delete the
|
|
|
|
|
object. They aren't meant for veto purposes, rather they allow you to
|
|
|
|
|
perform extra operations.
|
2010-11-21 19:00:40 +00:00
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.save_model(request, obj, form, change)
|
2008-08-09 20:52:40 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
The ``save_model`` method is given the ``HttpRequest``, a model instance,
|
2016-12-02 18:52:24 +00:00
|
|
|
|
a ``ModelForm`` instance, and a boolean value based on whether it is adding
|
|
|
|
|
or changing the object. Overriding this method allows doing pre- or
|
|
|
|
|
post-save operations. Call ``super().save_model()`` to save the object
|
|
|
|
|
using :meth:`.Model.save`.
|
2008-08-09 20:52:40 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
For example to attach ``request.user`` to the object prior to saving::
|
2008-08-09 20:52:40 +00:00
|
|
|
|
|
2013-05-19 10:42:35 +00:00
|
|
|
|
from django.contrib import admin
|
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
class ArticleAdmin(admin.ModelAdmin):
|
|
|
|
|
def save_model(self, request, obj, form, change):
|
|
|
|
|
obj.user = request.user
|
2017-01-22 06:57:14 +00:00
|
|
|
|
super().save_model(request, obj, form, change)
|
2008-08-09 20:52:40 +00:00
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.delete_model(request, obj)
|
2010-11-21 19:00:40 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
The ``delete_model`` method is given the ``HttpRequest`` and a model
|
2016-12-02 18:52:24 +00:00
|
|
|
|
instance. Overriding this method allows doing pre- or post-delete
|
|
|
|
|
operations. Call ``super().delete_model()`` to delete the object using
|
|
|
|
|
:meth:`.Model.delete`.
|
2010-11-21 19:00:40 +00:00
|
|
|
|
|
2017-09-04 09:24:34 +00:00
|
|
|
|
.. method:: ModelAdmin.delete_queryset(request, queryset)
|
|
|
|
|
|
|
|
|
|
.. versionadded:: 2.1
|
|
|
|
|
|
|
|
|
|
The ``delete_queryset()`` method is given the ``HttpRequest`` and a
|
|
|
|
|
``QuerySet`` of objects to be deleted. Override this method to customize
|
|
|
|
|
the deletion process for the "delete selected objects" :doc:`action
|
|
|
|
|
<actions>`.
|
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.save_formset(request, form, formset, change)
|
2008-08-09 20:52:40 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
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.
|
2008-08-09 20:52:40 +00:00
|
|
|
|
|
2014-10-29 15:31:40 +00:00
|
|
|
|
For example, to attach ``request.user`` to each changed formset
|
2010-12-05 04:52:31 +00:00
|
|
|
|
model instance::
|
2008-08-09 20:52:40 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
class ArticleAdmin(admin.ModelAdmin):
|
|
|
|
|
def save_formset(self, request, form, formset, change):
|
|
|
|
|
instances = formset.save(commit=False)
|
2015-04-20 14:45:05 +00:00
|
|
|
|
for obj in formset.deleted_objects:
|
|
|
|
|
obj.delete()
|
2010-12-05 04:52:31 +00:00
|
|
|
|
for instance in instances:
|
|
|
|
|
instance.user = request.user
|
|
|
|
|
instance.save()
|
|
|
|
|
formset.save_m2m()
|
2008-08-09 20:52:40 +00:00
|
|
|
|
|
2014-10-29 15:31:40 +00:00
|
|
|
|
See also :ref:`saving-objects-in-the-formset`.
|
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.get_ordering(request)
|
2011-06-12 13:04:53 +00:00
|
|
|
|
|
2015-05-20 11:00:19 +00:00
|
|
|
|
The ``get_ordering`` method takes a ``request`` as parameter and
|
2011-08-13 11:58:19 +00:00
|
|
|
|
is expected to return a ``list`` or ``tuple`` for ordering similar
|
2011-06-12 13:04:53 +00:00
|
|
|
|
to the :attr:`ordering` attribute. For example::
|
|
|
|
|
|
2013-05-19 10:42:35 +00:00
|
|
|
|
class PersonAdmin(admin.ModelAdmin):
|
2011-06-12 13:04:53 +00:00
|
|
|
|
|
|
|
|
|
def get_ordering(self, request):
|
|
|
|
|
if request.user.is_superuser:
|
|
|
|
|
return ['name', 'rank']
|
|
|
|
|
else:
|
|
|
|
|
return ['name']
|
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.get_search_results(request, queryset, search_term)
|
2013-05-19 08:52:29 +00:00
|
|
|
|
|
2015-05-21 15:07:38 +00:00
|
|
|
|
The ``get_search_results`` method modifies the list of objects displayed
|
|
|
|
|
into those that match the provided search term. It accepts the request, a
|
2013-05-19 08:52:29 +00:00
|
|
|
|
queryset that applies the current filters, and the user-provided search term.
|
|
|
|
|
It returns a tuple containing a queryset modified to implement the search, and
|
|
|
|
|
a boolean indicating if the results may contain duplicates.
|
|
|
|
|
|
|
|
|
|
The default implementation searches the fields named in :attr:`ModelAdmin.search_fields`.
|
|
|
|
|
|
|
|
|
|
This method may be overridden with your own custom search method. For
|
|
|
|
|
example, you might wish to search by an integer field, or use an external
|
|
|
|
|
tool such as Solr or Haystack. You must establish if the queryset changes
|
|
|
|
|
implemented by your search method may introduce duplicates into the results,
|
|
|
|
|
and return ``True`` in the second element of the return value.
|
|
|
|
|
|
2016-06-30 13:00:10 +00:00
|
|
|
|
For example, to search by ``name`` and ``age``, you could use::
|
2013-05-19 08:52:29 +00:00
|
|
|
|
|
|
|
|
|
class PersonAdmin(admin.ModelAdmin):
|
|
|
|
|
list_display = ('name', 'age')
|
|
|
|
|
search_fields = ('name',)
|
|
|
|
|
|
|
|
|
|
def get_search_results(self, request, queryset, search_term):
|
2017-01-22 06:57:14 +00:00
|
|
|
|
queryset, use_distinct = super().get_search_results(request, queryset, search_term)
|
2013-05-19 08:52:29 +00:00
|
|
|
|
try:
|
|
|
|
|
search_term_as_int = int(search_term)
|
2014-07-29 09:06:32 +00:00
|
|
|
|
except ValueError:
|
2013-05-19 08:52:29 +00:00
|
|
|
|
pass
|
2014-07-29 09:06:32 +00:00
|
|
|
|
else:
|
|
|
|
|
queryset |= self.model.objects.filter(age=search_term_as_int)
|
2013-05-19 08:52:29 +00:00
|
|
|
|
return queryset, use_distinct
|
|
|
|
|
|
2016-06-30 13:00:10 +00:00
|
|
|
|
This implementation is more efficient than ``search_fields =
|
|
|
|
|
('name', '=age')`` which results in a string comparison for the numeric
|
|
|
|
|
field, for example ``... OR UPPER("polls_choice"."votes"::text) = UPPER('4')``
|
|
|
|
|
on PostgreSQL.
|
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.save_related(request, form, formsets, change)
|
2011-07-03 17:56:43 +00:00
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
2017-05-10 12:48:57 +00:00
|
|
|
|
.. method:: ModelAdmin.get_autocomplete_fields(request)
|
|
|
|
|
|
2017-12-07 15:52:46 +00:00
|
|
|
|
The ``get_autocomplete_fields()`` method is given the ``HttpRequest`` and is
|
2017-05-10 12:48:57 +00:00
|
|
|
|
expected to return a ``list`` or ``tuple`` of field names that will be
|
|
|
|
|
displayed with an autocomplete widget as described above in the
|
|
|
|
|
:attr:`ModelAdmin.autocomplete_fields` section.
|
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.get_readonly_fields(request, obj=None)
|
2009-12-22 18:29:00 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
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.
|
2009-12-22 18:29:00 +00:00
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.get_prepopulated_fields(request, obj=None)
|
2011-04-22 12:02:25 +00:00
|
|
|
|
|
|
|
|
|
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.
|
2011-06-08 22:53:55 +00:00
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.get_list_display(request)
|
2011-06-08 22:53:55 +00:00
|
|
|
|
|
|
|
|
|
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.
|
2011-04-22 12:02:25 +00:00
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.get_list_display_links(request, list_display)
|
2011-10-26 12:19:18 +00:00
|
|
|
|
|
|
|
|
|
The ``get_list_display_links`` method is given the ``HttpRequest`` and
|
|
|
|
|
the ``list`` or ``tuple`` returned by :meth:`ModelAdmin.get_list_display`.
|
2013-09-06 18:25:13 +00:00
|
|
|
|
It is expected to return either ``None`` or 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.
|
|
|
|
|
|
2016-09-29 09:55:10 +00:00
|
|
|
|
.. method:: ModelAdmin.get_exclude(request, obj=None)
|
|
|
|
|
|
|
|
|
|
The ``get_exclude`` method is given the ``HttpRequest`` and the ``obj``
|
|
|
|
|
being edited (or ``None`` on an add form) and is expected to return a list
|
|
|
|
|
of fields, as described in :attr:`ModelAdmin.exclude`.
|
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.get_fields(request, obj=None)
|
2013-07-31 05:52:11 +00:00
|
|
|
|
|
|
|
|
|
The ``get_fields`` method is given the ``HttpRequest`` and the ``obj``
|
|
|
|
|
being edited (or ``None`` on an add form) and is expected to return a list
|
|
|
|
|
of fields, as described above in the :attr:`ModelAdmin.fields` section.
|
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.get_fieldsets(request, obj=None)
|
2013-02-02 21:21:50 +00:00
|
|
|
|
|
|
|
|
|
The ``get_fieldsets`` method is given the ``HttpRequest`` and the ``obj``
|
|
|
|
|
being edited (or ``None`` on an add form) and is expected to return a list
|
|
|
|
|
of two-tuples, in which each two-tuple represents a ``<fieldset>`` on the
|
|
|
|
|
admin form page, as described above in the :attr:`ModelAdmin.fieldsets` section.
|
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.get_list_filter(request)
|
2012-11-25 19:39:23 +00:00
|
|
|
|
|
|
|
|
|
The ``get_list_filter`` method is given the ``HttpRequest`` and is expected
|
|
|
|
|
to return the same kind of sequence type as for the
|
|
|
|
|
:attr:`~ModelAdmin.list_filter` attribute.
|
|
|
|
|
|
2015-03-16 10:00:16 +00:00
|
|
|
|
.. method:: ModelAdmin.get_list_select_related(request)
|
|
|
|
|
|
|
|
|
|
The ``get_list_select_related`` method is given the ``HttpRequest`` and
|
|
|
|
|
should return a boolean or list as :attr:`ModelAdmin.list_select_related`
|
|
|
|
|
does.
|
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.get_search_fields(request)
|
2013-08-03 16:15:15 +00:00
|
|
|
|
|
|
|
|
|
The ``get_search_fields`` method is given the ``HttpRequest`` and is expected
|
|
|
|
|
to return the same kind of sequence type as for the
|
|
|
|
|
:attr:`~ModelAdmin.search_fields` attribute.
|
|
|
|
|
|
2016-02-09 00:35:03 +00:00
|
|
|
|
.. method:: ModelAdmin.get_sortable_by(request)
|
|
|
|
|
|
|
|
|
|
.. versionadded:: 2.1
|
|
|
|
|
|
|
|
|
|
The ``get_sortable_by()`` method is passed the ``HttpRequest`` and is
|
|
|
|
|
expected to return a collection (e.g. ``list``, ``tuple``, or ``set``) of
|
|
|
|
|
field names that will be sortable in the change list page.
|
|
|
|
|
|
|
|
|
|
Its default implementation returns :attr:`sortable_by` if it's set,
|
|
|
|
|
otherwise it defers to :meth:`get_list_display`.
|
|
|
|
|
|
|
|
|
|
For example, to prevent one or more columns from being sortable::
|
|
|
|
|
|
|
|
|
|
class PersonAdmin(admin.ModelAdmin):
|
|
|
|
|
|
|
|
|
|
def get_sortable_by(self, request):
|
|
|
|
|
return {*self.get_list_display(request)} - {'rank'}
|
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.get_inline_instances(request, obj=None)
|
2012-10-20 13:48:38 +00:00
|
|
|
|
|
|
|
|
|
The ``get_inline_instances`` 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 :class:`~django.contrib.admin.InlineModelAdmin`
|
|
|
|
|
objects, as described below in the :class:`~django.contrib.admin.InlineModelAdmin`
|
2014-03-03 15:40:06 +00:00
|
|
|
|
section. For example, the following would return inlines without the default
|
2018-05-02 08:39:12 +00:00
|
|
|
|
filtering based on add, change, delete, and view permissions::
|
2014-03-03 15:40:06 +00:00
|
|
|
|
|
|
|
|
|
class MyModelAdmin(admin.ModelAdmin):
|
2014-11-25 18:13:45 +00:00
|
|
|
|
inlines = (MyInline,)
|
|
|
|
|
|
2014-07-22 05:47:57 +00:00
|
|
|
|
def get_inline_instances(self, request, obj=None):
|
2014-03-03 15:40:06 +00:00
|
|
|
|
return [inline(self.model, self.admin_site) for inline in self.inlines]
|
2012-10-20 13:48:38 +00:00
|
|
|
|
|
2014-11-25 18:13:45 +00:00
|
|
|
|
If you override this method, make sure that the returned inlines are
|
|
|
|
|
instances of the classes defined in :attr:`inlines` or you might encounter
|
|
|
|
|
a "Bad Request" error when adding related objects.
|
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.get_urls()
|
2009-01-14 20:22:25 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
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`::
|
2009-02-22 06:08:13 +00:00
|
|
|
|
|
2016-10-20 17:29:04 +00:00
|
|
|
|
from django.contrib import admin
|
|
|
|
|
from django.template.response import TemplateResponse
|
|
|
|
|
from django.urls import path
|
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
class MyModelAdmin(admin.ModelAdmin):
|
|
|
|
|
def get_urls(self):
|
2017-01-22 06:57:14 +00:00
|
|
|
|
urls = super().get_urls()
|
2014-04-02 00:46:34 +00:00
|
|
|
|
my_urls = [
|
2016-10-20 17:29:04 +00:00
|
|
|
|
path('my_view/', self.my_view),
|
2014-04-02 00:46:34 +00:00
|
|
|
|
]
|
2010-12-05 04:52:31 +00:00
|
|
|
|
return my_urls + urls
|
2009-02-22 06:08:13 +00:00
|
|
|
|
|
2010-12-29 21:50:40 +00:00
|
|
|
|
def my_view(self, request):
|
2015-02-11 10:22:13 +00:00
|
|
|
|
# ...
|
|
|
|
|
context = dict(
|
|
|
|
|
# Include common variables for rendering the admin template.
|
2015-04-09 15:27:09 +00:00
|
|
|
|
self.admin_site.each_context(request),
|
2015-02-11 10:22:13 +00:00
|
|
|
|
# Anything else you want in the context...
|
|
|
|
|
key=value,
|
|
|
|
|
)
|
|
|
|
|
return TemplateResponse(request, "sometemplate.html", context)
|
|
|
|
|
|
|
|
|
|
If you want to use the admin layout, extend from ``admin/base_site.html``:
|
|
|
|
|
|
|
|
|
|
.. code-block:: html+django
|
|
|
|
|
|
|
|
|
|
{% extends "admin/base_site.html" %}
|
|
|
|
|
{% block content %}
|
|
|
|
|
...
|
|
|
|
|
{% endblock %}
|
2010-12-29 21:50:40 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
.. note::
|
2009-01-14 20:22:25 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
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.
|
2009-01-14 20:22:25 +00:00
|
|
|
|
|
2010-12-29 21:50:40 +00:00
|
|
|
|
In this example, ``my_view`` will be accessed at
|
|
|
|
|
``/admin/myapp/mymodel/my_view/`` (assuming the admin URLs are included
|
|
|
|
|
at ``/admin/``.)
|
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
However, the ``self.my_view`` function registered above suffers from two
|
|
|
|
|
problems:
|
2009-07-13 13:46:31 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
* 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.
|
2009-07-13 13:46:31 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
Since this is usually not what you want, Django provides a convenience
|
|
|
|
|
wrapper to check permissions and mark the view as non-cacheable. This
|
2013-01-01 13:12:42 +00:00
|
|
|
|
wrapper is ``AdminSite.admin_view()`` (i.e. ``self.admin_site.admin_view``
|
|
|
|
|
inside a ``ModelAdmin`` instance); use it like so::
|
2009-01-14 20:22:25 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
class MyModelAdmin(admin.ModelAdmin):
|
|
|
|
|
def get_urls(self):
|
2017-01-22 06:57:14 +00:00
|
|
|
|
urls = super().get_urls()
|
2014-04-02 00:46:34 +00:00
|
|
|
|
my_urls = [
|
2016-10-20 17:29:04 +00:00
|
|
|
|
path('my_view/', self.admin_site.admin_view(self.my_view))
|
2014-04-02 00:46:34 +00:00
|
|
|
|
]
|
2010-12-05 04:52:31 +00:00
|
|
|
|
return my_urls + urls
|
2009-01-14 20:22:25 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
Notice the wrapped view in the fifth line above::
|
2009-01-14 20:22:25 +00:00
|
|
|
|
|
2016-10-20 17:29:04 +00:00
|
|
|
|
path('my_view/', self.admin_site.admin_view(self.my_view))
|
2009-02-22 06:08:13 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
This wrapping will protect ``self.my_view`` from unauthorized access and
|
2015-04-27 21:56:02 +00:00
|
|
|
|
will apply the :func:`django.views.decorators.cache.never_cache` decorator to
|
2010-12-05 04:52:31 +00:00
|
|
|
|
make sure it is not cached if the cache middleware is active.
|
2009-07-13 13:46:31 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
If the page is cacheable, but you still want the permission check to be
|
|
|
|
|
performed, you can pass a ``cacheable=True`` argument to
|
2013-01-01 13:12:42 +00:00
|
|
|
|
``AdminSite.admin_view()``::
|
2009-07-13 13:46:31 +00:00
|
|
|
|
|
2016-10-20 17:29:04 +00:00
|
|
|
|
path('my_view/', self.admin_site.admin_view(self.my_view, cacheable=True))
|
2009-01-14 20:22:25 +00:00
|
|
|
|
|
2014-12-27 07:16:53 +00:00
|
|
|
|
``ModelAdmin`` views have ``model_admin`` attributes. Other
|
|
|
|
|
``AdminSite`` views have ``admin_site`` attributes.
|
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.get_form(request, obj=None, **kwargs)
|
2012-10-19 00:12:41 +00:00
|
|
|
|
|
|
|
|
|
Returns a :class:`~django.forms.ModelForm` class for use in the admin add
|
|
|
|
|
and change views, see :meth:`add_view` and :meth:`change_view`.
|
|
|
|
|
|
2014-11-15 19:31:09 +00:00
|
|
|
|
The base implementation uses :func:`~django.forms.models.modelform_factory`
|
|
|
|
|
to subclass :attr:`~form`, modified by attributes such as :attr:`~fields`
|
|
|
|
|
and :attr:`~exclude`. So, for example, if you wanted to offer additional
|
|
|
|
|
fields to superusers, you could swap in a different base form like so::
|
2012-10-19 00:12:41 +00:00
|
|
|
|
|
|
|
|
|
class MyModelAdmin(admin.ModelAdmin):
|
|
|
|
|
def get_form(self, request, obj=None, **kwargs):
|
2014-11-15 19:31:09 +00:00
|
|
|
|
if request.user.is_superuser:
|
|
|
|
|
kwargs['form'] = MySuperuserForm
|
2017-01-22 06:57:14 +00:00
|
|
|
|
return super().get_form(request, obj, **kwargs)
|
2012-10-19 00:12:41 +00:00
|
|
|
|
|
2014-11-15 19:31:09 +00:00
|
|
|
|
You may also simply return a custom :class:`~django.forms.ModelForm` class
|
|
|
|
|
directly.
|
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.get_formsets_with_inlines(request, obj=None)
|
2013-09-04 01:01:45 +00:00
|
|
|
|
|
|
|
|
|
Yields (``FormSet``, :class:`InlineModelAdmin`) pairs for use in admin add
|
|
|
|
|
and change views.
|
|
|
|
|
|
|
|
|
|
For example if you wanted to display a particular inline only in the change
|
|
|
|
|
view, you could override ``get_formsets_with_inlines`` as follows::
|
|
|
|
|
|
|
|
|
|
class MyModelAdmin(admin.ModelAdmin):
|
|
|
|
|
inlines = [MyInline, SomeOtherInline]
|
|
|
|
|
|
|
|
|
|
def get_formsets_with_inlines(self, request, obj=None):
|
|
|
|
|
for inline in self.get_inline_instances(request, obj):
|
|
|
|
|
# hide MyInline in the add view
|
2018-01-12 14:05:16 +00:00
|
|
|
|
if not isinstance(inline, MyInline) or obj is not None:
|
|
|
|
|
yield inline.get_formset(request, obj), inline
|
2013-09-04 01:01:45 +00:00
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.formfield_for_foreignkey(db_field, request, **kwargs)
|
2009-01-16 15:32:31 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
The ``formfield_for_foreignkey`` method on a ``ModelAdmin`` allows you to
|
2011-06-12 13:04:53 +00:00
|
|
|
|
override the default formfield for a foreign keys field. For example, to
|
2010-12-05 04:52:31 +00:00
|
|
|
|
return a subset of objects for this foreign key field based on the user::
|
2009-01-16 15:32:31 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
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)
|
2017-01-22 06:57:14 +00:00
|
|
|
|
return super().formfield_for_foreignkey(db_field, request, **kwargs)
|
2009-01-16 15:32:31 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
This uses the ``HttpRequest`` instance to filter the ``Car`` foreign key
|
|
|
|
|
field to only display the cars owned by the ``User`` instance.
|
2010-08-07 14:56:16 +00:00
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.formfield_for_manytomany(db_field, request, **kwargs)
|
2010-08-07 14:56:16 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
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``::
|
2010-08-07 14:56:16 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
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)
|
2017-01-22 06:57:14 +00:00
|
|
|
|
return super().formfield_for_manytomany(db_field, request, **kwargs)
|
2010-12-27 13:41:19 +00:00
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.formfield_for_choice_field(db_field, request, **kwargs)
|
2011-02-02 20:57:09 +00:00
|
|
|
|
|
|
|
|
|
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'),)
|
2017-01-22 06:57:14 +00:00
|
|
|
|
return super().formfield_for_choice_field(db_field, request, **kwargs)
|
2011-02-02 20:57:09 +00:00
|
|
|
|
|
2013-07-24 21:58:14 +00:00
|
|
|
|
.. admonition:: Note
|
|
|
|
|
|
2015-05-21 15:07:38 +00:00
|
|
|
|
Any ``choices`` attribute set on the formfield will be limited to the
|
|
|
|
|
form field only. If the corresponding field on the model has choices
|
|
|
|
|
set, the choices provided to the form must be a valid subset of those
|
2013-07-24 21:58:14 +00:00
|
|
|
|
choices, otherwise the form submission will fail with
|
|
|
|
|
a :exc:`~django.core.exceptions.ValidationError` when the model itself
|
|
|
|
|
is validated before saving.
|
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.get_changelist(request, **kwargs)
|
2012-07-08 22:35:17 +00:00
|
|
|
|
|
2012-11-03 09:22:34 +00:00
|
|
|
|
Returns the ``Changelist`` class to be used for listing. By default,
|
2012-07-08 22:35:17 +00:00
|
|
|
|
``django.contrib.admin.views.main.ChangeList`` is used. By inheriting this
|
|
|
|
|
class you can change the behavior of the listing.
|
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.get_changelist_form(request, **kwargs)
|
2012-11-03 09:22:34 +00:00
|
|
|
|
|
|
|
|
|
Returns a :class:`~django.forms.ModelForm` class for use in the ``Formset``
|
|
|
|
|
on the changelist page. To use a custom form, for example::
|
|
|
|
|
|
2013-05-19 10:42:35 +00:00
|
|
|
|
from django import forms
|
|
|
|
|
|
2012-11-03 09:22:34 +00:00
|
|
|
|
class MyForm(forms.ModelForm):
|
2013-02-21 21:56:55 +00:00
|
|
|
|
pass
|
2012-11-03 09:22:34 +00:00
|
|
|
|
|
|
|
|
|
class MyModelAdmin(admin.ModelAdmin):
|
|
|
|
|
def get_changelist_form(self, request, **kwargs):
|
|
|
|
|
return MyForm
|
|
|
|
|
|
2013-02-21 21:56:55 +00:00
|
|
|
|
.. admonition:: Note
|
|
|
|
|
|
|
|
|
|
If you define the ``Meta.model`` attribute on a
|
|
|
|
|
:class:`~django.forms.ModelForm`, you must also define the
|
|
|
|
|
``Meta.fields`` attribute (or the ``Meta.exclude`` attribute). However,
|
|
|
|
|
``ModelAdmin`` ignores this value, overriding it with the
|
|
|
|
|
:attr:`ModelAdmin.list_editable` attribute. The easiest solution is to
|
|
|
|
|
omit the ``Meta.model`` attribute, since ``ModelAdmin`` will provide the
|
|
|
|
|
correct model to use.
|
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.get_changelist_formset(request, **kwargs)
|
2012-11-03 09:22:34 +00:00
|
|
|
|
|
|
|
|
|
Returns a :ref:`ModelFormSet <model-formsets>` class for use on the
|
|
|
|
|
changelist page if :attr:`~ModelAdmin.list_editable` is used. To use a
|
|
|
|
|
custom formset, for example::
|
|
|
|
|
|
2015-10-28 01:37:35 +00:00
|
|
|
|
from django.forms import BaseModelFormSet
|
2012-11-03 09:22:34 +00:00
|
|
|
|
|
|
|
|
|
class MyAdminFormSet(BaseModelFormSet):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
class MyModelAdmin(admin.ModelAdmin):
|
|
|
|
|
def get_changelist_formset(self, request, **kwargs):
|
|
|
|
|
kwargs['formset'] = MyAdminFormSet
|
2017-01-22 06:57:14 +00:00
|
|
|
|
return super().get_changelist_formset(request, **kwargs)
|
2012-11-03 09:22:34 +00:00
|
|
|
|
|
2017-08-15 00:48:51 +00:00
|
|
|
|
.. method:: ModelAdmin.lookup_allowed(lookup, value)
|
|
|
|
|
|
|
|
|
|
The objects in the changelist page can be filtered with lookups from the
|
|
|
|
|
URL's query string. This is how :attr:`list_filter` works, for example. The
|
|
|
|
|
lookups are similar to what's used in :meth:`.QuerySet.filter` (e.g.
|
|
|
|
|
``user__email=user@example.com``). Since the lookups in the query string
|
|
|
|
|
can be manipulated by the user, they must be sanitized to prevent
|
|
|
|
|
unauthorized data exposure.
|
|
|
|
|
|
|
|
|
|
The ``lookup_allowed()`` method is given a lookup path from the query string
|
|
|
|
|
(e.g. ``'user__email'``) and the corresponding value
|
|
|
|
|
(e.g. ``'user@example.com'``), and returns a boolean indicating whether
|
|
|
|
|
filtering the changelist's ``QuerySet`` using the parameters is permitted.
|
|
|
|
|
If ``lookup_allowed()`` returns ``False``, ``DisallowedModelAdminLookup``
|
|
|
|
|
(subclass of :exc:`~django.core.exceptions.SuspiciousOperation`) is raised.
|
|
|
|
|
|
|
|
|
|
By default, ``lookup_allowed()`` allows access to a model's local fields,
|
|
|
|
|
field paths used in :attr:`~ModelAdmin.list_filter` (but not paths from
|
|
|
|
|
:meth:`~ModelAdmin.get_list_filter`), and lookups required for
|
|
|
|
|
:attr:`~django.db.models.ForeignKey.limit_choices_to` to function
|
|
|
|
|
correctly in :attr:`~django.contrib.admin.ModelAdmin.raw_id_fields`.
|
|
|
|
|
|
|
|
|
|
Override this method to customize the lookups permitted for your
|
|
|
|
|
:class:`~django.contrib.admin.ModelAdmin` subclass.
|
|
|
|
|
|
2018-05-02 08:39:12 +00:00
|
|
|
|
.. method:: ModelAdmin.has_view_permission(request, obj=None)
|
|
|
|
|
|
|
|
|
|
.. versionadded:: 2.1
|
|
|
|
|
|
|
|
|
|
Should return ``True`` if viewing ``obj`` is permitted, ``False`` otherwise.
|
|
|
|
|
If obj is ``None``, should return ``True`` or ``False`` to indicate whether
|
|
|
|
|
viewing 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
|
|
|
|
|
view any object of this type).
|
|
|
|
|
|
|
|
|
|
The default implementation returns ``True`` if the user has either the
|
|
|
|
|
"change" or "view" permission.
|
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.has_add_permission(request)
|
2010-12-27 13:41:19 +00:00
|
|
|
|
|
|
|
|
|
Should return ``True`` if adding an object is permitted, ``False``
|
|
|
|
|
otherwise.
|
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.has_change_permission(request, obj=None)
|
2010-12-27 13:41:19 +00:00
|
|
|
|
|
2018-02-22 16:15:04 +00:00
|
|
|
|
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).
|
2010-12-27 13:41:19 +00:00
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.has_delete_permission(request, obj=None)
|
2010-12-27 13:41:19 +00:00
|
|
|
|
|
2018-02-22 16:15:04 +00:00
|
|
|
|
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).
|
2009-01-16 15:32:31 +00:00
|
|
|
|
|
2014-05-28 16:07:27 +00:00
|
|
|
|
.. method:: ModelAdmin.has_module_permission(request)
|
|
|
|
|
|
|
|
|
|
Should return ``True`` if displaying the module on the admin index page and
|
|
|
|
|
accessing the module's index page is permitted, ``False`` otherwise.
|
|
|
|
|
Uses :meth:`User.has_module_perms()
|
|
|
|
|
<django.contrib.auth.models.User.has_module_perms>` by default. Overriding
|
2018-05-02 08:39:12 +00:00
|
|
|
|
it does not restrict access to the view, add, change, or delete views,
|
|
|
|
|
:meth:`~ModelAdmin.has_view_permission`,
|
2014-05-28 16:07:27 +00:00
|
|
|
|
:meth:`~ModelAdmin.has_add_permission`,
|
|
|
|
|
:meth:`~ModelAdmin.has_change_permission`, and
|
|
|
|
|
:meth:`~ModelAdmin.has_delete_permission` should be used for that.
|
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.get_queryset(request)
|
2010-05-09 06:44:52 +00:00
|
|
|
|
|
2013-03-08 14:15:23 +00:00
|
|
|
|
The ``get_queryset`` method on a ``ModelAdmin`` returns a
|
2011-09-30 10:28:39 +00:00
|
|
|
|
: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::
|
2010-05-09 06:44:52 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
class MyModelAdmin(admin.ModelAdmin):
|
2013-03-08 14:15:23 +00:00
|
|
|
|
def get_queryset(self, request):
|
2017-01-22 06:57:14 +00:00
|
|
|
|
qs = super().get_queryset(request)
|
2010-12-05 04:52:31 +00:00
|
|
|
|
if request.user.is_superuser:
|
|
|
|
|
return qs
|
|
|
|
|
return qs.filter(author=request.user)
|
2010-05-09 06:44:52 +00:00
|
|
|
|
|
2012-11-17 21:53:31 +00:00
|
|
|
|
.. method:: ModelAdmin.message_user(request, message, level=messages.INFO, extra_tags='', fail_silently=False)
|
2010-12-11 13:51:14 +00:00
|
|
|
|
|
2012-11-17 21:53:31 +00:00
|
|
|
|
Sends a message to the user using the :mod:`django.contrib.messages`
|
|
|
|
|
backend. See the :ref:`custom ModelAdmin example <custom-admin-action>`.
|
|
|
|
|
|
|
|
|
|
Keyword arguments allow you to change the message level, add extra CSS
|
|
|
|
|
tags, or fail silently if the ``contrib.messages`` framework is not
|
|
|
|
|
installed. These keyword arguments match those for
|
|
|
|
|
:func:`django.contrib.messages.add_message`, see that function's
|
|
|
|
|
documentation for more details. One difference is that the level may be
|
|
|
|
|
passed as a string label in addition to integer/constant.
|
2010-12-11 13:51:14 +00:00
|
|
|
|
|
2015-07-18 10:35:49 +00:00
|
|
|
|
.. method:: ModelAdmin.get_paginator(request, queryset, per_page, orphans=0, allow_empty_first_page=True)
|
2010-12-21 14:57:29 +00:00
|
|
|
|
|
|
|
|
|
Returns an instance of the paginator to use for this view. By default,
|
|
|
|
|
instantiates an instance of :attr:`paginator`.
|
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.response_add(request, obj, post_url_continue=None)
|
2013-09-07 00:07:51 +00:00
|
|
|
|
|
2013-09-07 16:33:10 +00:00
|
|
|
|
Determines the :class:`~django.http.HttpResponse` for the
|
|
|
|
|
:meth:`add_view` stage.
|
2013-09-07 00:07:51 +00:00
|
|
|
|
|
|
|
|
|
``response_add`` is called after the admin form is submitted and
|
|
|
|
|
just after the object and all the related instances have
|
|
|
|
|
been created and saved. You can override it to change the default behavior
|
|
|
|
|
after the object has been created.
|
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.response_change(request, obj)
|
2013-09-07 00:07:51 +00:00
|
|
|
|
|
2013-09-07 16:33:10 +00:00
|
|
|
|
Determines the :class:`~django.http.HttpResponse` for the
|
|
|
|
|
:meth:`change_view` stage.
|
2013-09-07 00:07:51 +00:00
|
|
|
|
|
|
|
|
|
``response_change`` is called after the admin form is submitted and
|
|
|
|
|
just after the object and all the related instances have
|
|
|
|
|
been saved. You can override it to change the default
|
2014-02-22 17:30:28 +00:00
|
|
|
|
behavior after the object has been changed.
|
2013-09-07 00:07:51 +00:00
|
|
|
|
|
2013-11-17 22:26:20 +00:00
|
|
|
|
.. method:: ModelAdmin.response_delete(request, obj_display, obj_id)
|
2013-09-07 00:07:51 +00:00
|
|
|
|
|
2013-09-07 16:33:10 +00:00
|
|
|
|
Determines the :class:`~django.http.HttpResponse` for the
|
|
|
|
|
:meth:`delete_view` stage.
|
2013-09-07 00:07:51 +00:00
|
|
|
|
|
|
|
|
|
``response_delete`` is called after the object has been
|
|
|
|
|
deleted. You can override it to change the default
|
|
|
|
|
behavior after the object has been deleted.
|
|
|
|
|
|
|
|
|
|
``obj_display`` is a string with the name of the deleted
|
|
|
|
|
object.
|
|
|
|
|
|
2013-11-17 22:26:20 +00:00
|
|
|
|
``obj_id`` is the serialized identifier used to retrieve the object to be
|
|
|
|
|
deleted.
|
|
|
|
|
|
2014-02-26 22:34:19 +00:00
|
|
|
|
.. method:: ModelAdmin.get_changeform_initial_data(request)
|
|
|
|
|
|
|
|
|
|
A hook for the initial data on admin change forms. By default, fields are
|
|
|
|
|
given initial values from ``GET`` parameters. For instance,
|
|
|
|
|
``?name=initial_value`` will set the ``name`` field's initial value to be
|
|
|
|
|
``initial_value``.
|
|
|
|
|
|
|
|
|
|
This method should return a dictionary in the form
|
|
|
|
|
``{'fieldname': 'fieldval'}``::
|
|
|
|
|
|
|
|
|
|
def get_changeform_initial_data(self, request):
|
|
|
|
|
return {'name': 'custom_initial_value'}
|
|
|
|
|
|
2017-03-30 09:13:15 +00:00
|
|
|
|
.. method:: ModelAdmin.get_deleted_objects(objs, request)
|
|
|
|
|
|
|
|
|
|
.. versionadded:: 2.1
|
|
|
|
|
|
|
|
|
|
A hook for customizing the deletion process of the :meth:`delete_view` and
|
|
|
|
|
the "delete selected" :doc:`action <actions>`.
|
|
|
|
|
|
|
|
|
|
The ``objs`` argument is a homogeneous iterable of objects (a ``QuerySet``
|
|
|
|
|
or a list of model instances) to be deleted, and ``request`` is the
|
|
|
|
|
:class:`~django.http.HttpRequest`.
|
|
|
|
|
|
|
|
|
|
This method must return a 4-tuple of
|
|
|
|
|
``(deleted_objects, model_count, perms_needed, protected)``.
|
|
|
|
|
|
|
|
|
|
``deleted_objects`` is a list of strings representing all the objects that
|
|
|
|
|
will be deleted. If there are any related objects to be deleted, the list
|
|
|
|
|
is nested and includes those related objects. The list is formatted in the
|
|
|
|
|
template using the :tfilter:`unordered_list` filter.
|
|
|
|
|
|
|
|
|
|
``model_count`` is a dictionary mapping each model's
|
|
|
|
|
:attr:`~django.db.models.Options.verbose_name_plural` to the number of
|
|
|
|
|
objects that will be deleted.
|
|
|
|
|
|
|
|
|
|
``perms_needed`` is a set of :attr:`~django.db.models.Options.verbose_name`\s
|
|
|
|
|
of the models that the user doesn't have permission to delete.
|
|
|
|
|
|
|
|
|
|
``protected`` is a list of strings representing of all the protected
|
|
|
|
|
related objects that can't be deleted. The list is displayed in the
|
|
|
|
|
template.
|
|
|
|
|
|
2009-06-24 14:02:22 +00:00
|
|
|
|
Other methods
|
|
|
|
|
~~~~~~~~~~~~~
|
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.add_view(request, form_url='', extra_context=None)
|
2009-06-24 14:02:22 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
Django view for the model instance addition page. See note below.
|
2009-06-24 14:02:22 +00:00
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.change_view(request, object_id, form_url='', extra_context=None)
|
2009-06-24 14:02:22 +00:00
|
|
|
|
|
2016-04-28 22:19:21 +00:00
|
|
|
|
Django view for the model instance editing page. See note below.
|
2009-06-24 14:02:22 +00:00
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.changelist_view(request, extra_context=None)
|
2009-06-24 14:02:22 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
Django view for the model instances change list/actions page. See note
|
|
|
|
|
below.
|
2009-06-24 14:02:22 +00:00
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.delete_view(request, object_id, extra_context=None)
|
2009-06-24 14:02:22 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
Django view for the model instance(s) deletion confirmation page. See note
|
|
|
|
|
below.
|
2009-06-24 14:02:22 +00:00
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: ModelAdmin.history_view(request, object_id, extra_context=None)
|
2009-06-24 14:02:22 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
Django view for the page that shows the modification history for a given
|
|
|
|
|
model instance.
|
2009-06-24 14:02:22 +00:00
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
2009-09-12 22:44:51 +00:00
|
|
|
|
One common reason for overriding these methods is to augment the context data
|
2009-06-24 14:02:22 +00:00
|
|
|
|
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):
|
|
|
|
|
# ...
|
2011-01-16 13:56:12 +00:00
|
|
|
|
pass
|
2009-06-24 14:02:22 +00:00
|
|
|
|
|
2012-07-25 20:33:38 +00:00
|
|
|
|
def change_view(self, request, object_id, form_url='', extra_context=None):
|
2012-02-24 22:50:36 +00:00
|
|
|
|
extra_context = extra_context or {}
|
|
|
|
|
extra_context['osm_data'] = self.get_osm_info()
|
2017-01-22 06:57:14 +00:00
|
|
|
|
return super().change_view(
|
2016-06-02 19:56:13 +00:00
|
|
|
|
request, object_id, form_url, extra_context=extra_context,
|
|
|
|
|
)
|
2009-06-24 14:02:22 +00:00
|
|
|
|
|
2012-12-26 20:47:29 +00:00
|
|
|
|
These views return :class:`~django.template.response.TemplateResponse`
|
2011-04-22 18:17:16 +00:00
|
|
|
|
instances which allow you to easily customize the response data before
|
2012-12-26 20:47:29 +00:00
|
|
|
|
rendering. For more details, see the :doc:`TemplateResponse documentation
|
|
|
|
|
</ref/template-response>`.
|
2011-04-22 18:17:16 +00:00
|
|
|
|
|
2013-06-20 06:51:20 +00:00
|
|
|
|
.. _modeladmin-asset-definitions:
|
2012-11-17 22:25:52 +00:00
|
|
|
|
|
2013-06-20 06:51:20 +00:00
|
|
|
|
``ModelAdmin`` asset definitions
|
2008-07-18 23:54:34 +00:00
|
|
|
|
--------------------------------
|
|
|
|
|
|
2008-07-27 18:34:21 +00:00
|
|
|
|
There are times where you would like add a bit of CSS and/or JavaScript to
|
2013-06-20 06:51:20 +00:00
|
|
|
|
the add/change views. This can be accomplished by using a ``Media`` inner class
|
2008-07-18 23:54:34 +00:00
|
|
|
|
on your ``ModelAdmin``::
|
|
|
|
|
|
|
|
|
|
class ArticleAdmin(admin.ModelAdmin):
|
|
|
|
|
class Media:
|
|
|
|
|
css = {
|
|
|
|
|
"all": ("my_styles.css",)
|
|
|
|
|
}
|
|
|
|
|
js = ("my_code.js",)
|
|
|
|
|
|
2011-01-16 13:56:12 +00:00
|
|
|
|
The :doc:`staticfiles app </ref/contrib/staticfiles>` prepends
|
|
|
|
|
:setting:`STATIC_URL` (or :setting:`MEDIA_URL` if :setting:`STATIC_URL` is
|
2013-06-20 06:51:20 +00:00
|
|
|
|
``None``) to any asset paths. The same rules apply as :ref:`regular asset
|
|
|
|
|
definitions on forms <form-asset-paths>`.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2015-06-18 16:27:58 +00:00
|
|
|
|
.. _contrib-admin-jquery:
|
|
|
|
|
|
2013-01-13 20:11:24 +00:00
|
|
|
|
jQuery
|
|
|
|
|
~~~~~~
|
|
|
|
|
|
2015-04-30 19:39:29 +00:00
|
|
|
|
Django admin JavaScript makes use of the `jQuery`_ library.
|
2013-01-15 20:47:31 +00:00
|
|
|
|
|
|
|
|
|
To avoid conflicts with user-supplied scripts or libraries, Django's jQuery
|
2018-05-13 17:02:24 +00:00
|
|
|
|
(version 3.3.1) is namespaced as ``django.jQuery``. If you want to use jQuery
|
2013-01-15 20:47:31 +00:00
|
|
|
|
in your own admin JavaScript without including a second copy, you can use the
|
|
|
|
|
``django.jQuery`` object on changelist and add/edit views.
|
|
|
|
|
|
2017-12-21 20:07:17 +00:00
|
|
|
|
.. versionchanged:: 2.1
|
|
|
|
|
|
2018-05-13 17:02:24 +00:00
|
|
|
|
jQuery was upgraded from 2.2.3 to 3.3.1.
|
2017-12-21 20:07:17 +00:00
|
|
|
|
|
2013-01-15 20:47:31 +00:00
|
|
|
|
The :class:`ModelAdmin` class requires jQuery by default, so there is no need
|
2013-08-05 16:23:26 +00:00
|
|
|
|
to add jQuery to your ``ModelAdmin``’s list of media resources unless you have
|
2014-02-28 16:44:03 +00:00
|
|
|
|
a specific need. For example, if you require the jQuery library to be in the
|
2013-01-15 20:47:31 +00:00
|
|
|
|
global namespace (for example when using third-party jQuery plugins) or if you
|
|
|
|
|
need a newer version of jQuery, you will have to include your own copy.
|
2010-04-19 10:16:25 +00:00
|
|
|
|
|
2013-01-13 20:11:24 +00:00
|
|
|
|
Django provides both uncompressed and 'minified' versions of jQuery, as
|
|
|
|
|
``jquery.js`` and ``jquery.min.js`` respectively.
|
|
|
|
|
|
|
|
|
|
:class:`ModelAdmin` and :class:`InlineModelAdmin` have a ``media`` property
|
|
|
|
|
that returns a list of ``Media`` objects which store paths to the JavaScript
|
|
|
|
|
files for the forms and/or formsets. If :setting:`DEBUG` is ``True`` it will
|
|
|
|
|
return the uncompressed versions of the various JavaScript files, including
|
|
|
|
|
``jquery.js``; if not, it will return the 'minified' versions.
|
|
|
|
|
|
2015-11-29 16:29:46 +00:00
|
|
|
|
.. _jQuery: https://jquery.com
|
2010-04-19 10:16:25 +00:00
|
|
|
|
|
2014-10-06 12:59:04 +00:00
|
|
|
|
.. _admin-custom-validation:
|
|
|
|
|
|
2008-08-04 19:29:33 +00:00
|
|
|
|
Adding custom validation to the admin
|
|
|
|
|
-------------------------------------
|
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
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::
|
2008-08-04 19:29:33 +00:00
|
|
|
|
|
|
|
|
|
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::
|
2009-02-22 06:08:13 +00:00
|
|
|
|
|
2008-08-04 19:29:33 +00:00
|
|
|
|
class MyArticleAdminForm(forms.ModelForm):
|
|
|
|
|
def clean_name(self):
|
|
|
|
|
# do something that validates your data
|
|
|
|
|
return self.cleaned_data["name"]
|
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
It is important you use a ``ModelForm`` here otherwise things can break. See
|
|
|
|
|
the :doc:`forms </ref/forms/index>` documentation on :doc:`custom validation
|
2010-08-19 19:27:44 +00:00
|
|
|
|
</ref/forms/validation>` and, more specifically, the
|
2009-06-24 14:04:18 +00:00
|
|
|
|
:ref:`model form validation notes <overriding-modelform-clean-method>` for more
|
|
|
|
|
information.
|
2008-08-04 19:29:33 +00:00
|
|
|
|
|
2008-09-02 16:27:47 +00:00
|
|
|
|
.. _admin-inlines:
|
|
|
|
|
|
2008-07-18 23:54:34 +00:00
|
|
|
|
``InlineModelAdmin`` objects
|
|
|
|
|
============================
|
|
|
|
|
|
2010-08-23 08:07:35 +00:00
|
|
|
|
.. class:: InlineModelAdmin
|
2011-02-16 01:56:53 +00:00
|
|
|
|
.. class:: TabularInline
|
|
|
|
|
.. class:: StackedInline
|
2010-08-23 08:07:35 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
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::
|
2009-03-31 23:34:03 +00:00
|
|
|
|
|
2013-05-19 10:42:35 +00:00
|
|
|
|
from django.db import models
|
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
class Author(models.Model):
|
|
|
|
|
name = models.CharField(max_length=100)
|
2009-03-31 23:34:03 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
class Book(models.Model):
|
2015-07-22 14:43:21 +00:00
|
|
|
|
author = models.ForeignKey(Author, on_delete=models.CASCADE)
|
2010-12-05 04:52:31 +00:00
|
|
|
|
title = models.CharField(max_length=100)
|
2009-03-31 23:34:03 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
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``::
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2013-05-19 10:42:35 +00:00
|
|
|
|
from django.contrib import admin
|
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
class BookInline(admin.TabularInline):
|
|
|
|
|
model = Book
|
2008-07-27 18:36:08 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
class AuthorAdmin(admin.ModelAdmin):
|
|
|
|
|
inlines = [
|
|
|
|
|
BookInline,
|
|
|
|
|
]
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
Django provides two subclasses of ``InlineModelAdmin`` and they are:
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
* :class:`~django.contrib.admin.TabularInline`
|
|
|
|
|
* :class:`~django.contrib.admin.StackedInline`
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
The difference between these two is merely the template used to render
|
|
|
|
|
them.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
|
|
|
|
``InlineModelAdmin`` options
|
|
|
|
|
-----------------------------
|
|
|
|
|
|
2010-12-12 22:56:01 +00:00
|
|
|
|
``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`
|
2012-06-07 13:02:35 +00:00
|
|
|
|
- :attr:`~ModelAdmin.formfield_overrides`
|
2010-12-12 22:56:01 +00:00
|
|
|
|
- :attr:`~ModelAdmin.exclude`
|
|
|
|
|
- :attr:`~ModelAdmin.filter_horizontal`
|
|
|
|
|
- :attr:`~ModelAdmin.filter_vertical`
|
2012-09-19 20:39:14 +00:00
|
|
|
|
- :attr:`~ModelAdmin.ordering`
|
2010-12-12 22:56:01 +00:00
|
|
|
|
- :attr:`~ModelAdmin.prepopulated_fields`
|
2013-03-08 14:15:23 +00:00
|
|
|
|
- :meth:`~ModelAdmin.get_queryset`
|
2010-12-12 22:56:01 +00:00
|
|
|
|
- :attr:`~ModelAdmin.radio_fields`
|
2012-06-07 13:02:35 +00:00
|
|
|
|
- :attr:`~ModelAdmin.readonly_fields`
|
2010-12-12 22:56:01 +00:00
|
|
|
|
- :attr:`~InlineModelAdmin.raw_id_fields`
|
2014-05-12 10:59:23 +00:00
|
|
|
|
- :meth:`~ModelAdmin.formfield_for_choice_field`
|
2010-12-12 22:56:01 +00:00
|
|
|
|
- :meth:`~ModelAdmin.formfield_for_foreignkey`
|
|
|
|
|
- :meth:`~ModelAdmin.formfield_for_manytomany`
|
2014-05-28 16:07:27 +00:00
|
|
|
|
- :meth:`~ModelAdmin.has_module_permission`
|
2011-10-07 00:41:25 +00:00
|
|
|
|
|
2018-04-02 21:14:16 +00:00
|
|
|
|
The ``InlineModelAdmin`` class adds or customizes:
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-07-30 04:15:16 +00:00
|
|
|
|
.. attribute:: InlineModelAdmin.model
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2011-10-07 00:41:25 +00:00
|
|
|
|
The model which the inline is using. This is required.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-07-30 04:15:16 +00:00
|
|
|
|
.. attribute:: InlineModelAdmin.fk_name
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-07-30 04:15:16 +00:00
|
|
|
|
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.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-07-30 04:15:16 +00:00
|
|
|
|
.. attribute:: InlineModelAdmin.formset
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2013-07-08 12:29:28 +00:00
|
|
|
|
This defaults to :class:`~django.forms.models.BaseInlineFormSet`. Using
|
|
|
|
|
your own formset can give you many possibilities of customization. Inlines
|
|
|
|
|
are built around :ref:`model formsets <model-formsets>`.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-07-30 04:15:16 +00:00
|
|
|
|
.. attribute:: InlineModelAdmin.form
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-07-30 04:15:16 +00:00
|
|
|
|
The value for ``form`` defaults to ``ModelForm``. This is what is passed
|
2013-01-22 11:46:22 +00:00
|
|
|
|
through to :func:`~django.forms.models.inlineformset_factory` when
|
|
|
|
|
creating the formset for this inline.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2013-11-15 02:26:19 +00:00
|
|
|
|
.. warning::
|
|
|
|
|
When writing custom validation for ``InlineModelAdmin`` forms, be cautious
|
|
|
|
|
of writing validation that relies on features of the parent model. If the
|
|
|
|
|
parent model fails to validate, it may be left in an inconsistent state as
|
|
|
|
|
described in the warning in :ref:`validation-on-modelform`.
|
|
|
|
|
|
2015-11-07 15:46:50 +00:00
|
|
|
|
.. attribute:: InlineModelAdmin.classes
|
|
|
|
|
|
|
|
|
|
A list or tuple containing extra CSS classes to apply to the fieldset that
|
|
|
|
|
is rendered for the inlines. Defaults to ``None``. As with classes
|
|
|
|
|
configured in :attr:`~ModelAdmin.fieldsets`, inlines with a ``collapse``
|
|
|
|
|
class will be initially collapsed and their header will have a small "show"
|
|
|
|
|
link.
|
|
|
|
|
|
2010-07-30 04:15:16 +00:00
|
|
|
|
.. attribute:: InlineModelAdmin.extra
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
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.
|
2010-07-30 04:15:16 +00:00
|
|
|
|
|
|
|
|
|
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.
|
2010-05-12 11:56:42 +00:00
|
|
|
|
|
2010-07-30 04:15:16 +00:00
|
|
|
|
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.
|
2010-05-12 11:56:42 +00:00
|
|
|
|
|
2013-05-30 17:48:10 +00:00
|
|
|
|
:meth:`InlineModelAdmin.get_extra` also allows you to customize the number
|
|
|
|
|
of extra forms.
|
|
|
|
|
|
2010-07-30 04:15:16 +00:00
|
|
|
|
.. attribute:: InlineModelAdmin.max_num
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-07-30 04:15:16 +00:00
|
|
|
|
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.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2013-06-01 22:16:57 +00:00
|
|
|
|
:meth:`InlineModelAdmin.get_max_num` also allows you to customize the
|
|
|
|
|
maximum number of extra forms.
|
|
|
|
|
|
2014-03-05 20:19:40 +00:00
|
|
|
|
.. attribute:: InlineModelAdmin.min_num
|
|
|
|
|
|
|
|
|
|
This controls the minimum number of forms to show in the inline.
|
|
|
|
|
See :func:`~django.forms.models.modelformset_factory` for more information.
|
|
|
|
|
|
|
|
|
|
:meth:`InlineModelAdmin.get_min_num` also allows you to customize the
|
|
|
|
|
minimum number of displayed forms.
|
|
|
|
|
|
2010-07-30 04:15:16 +00:00
|
|
|
|
.. attribute:: InlineModelAdmin.raw_id_fields
|
2008-08-01 19:47:26 +00:00
|
|
|
|
|
2010-07-30 04:15:16 +00:00
|
|
|
|
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.
|
2008-08-01 19:47:26 +00:00
|
|
|
|
|
2015-05-21 15:07:38 +00:00
|
|
|
|
``raw_id_fields`` is a list of fields you would like to change into an
|
2010-07-30 04:15:16 +00:00
|
|
|
|
``Input`` widget for either a ``ForeignKey`` or ``ManyToManyField``::
|
2008-08-01 19:47:26 +00:00
|
|
|
|
|
2010-07-30 04:15:16 +00:00
|
|
|
|
class BookInline(admin.TabularInline):
|
|
|
|
|
model = Book
|
|
|
|
|
raw_id_fields = ("pages",)
|
2008-08-01 19:47:26 +00:00
|
|
|
|
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-07-30 04:15:16 +00:00
|
|
|
|
.. attribute:: InlineModelAdmin.template
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-07-30 04:15:16 +00:00
|
|
|
|
The template used to render the inline on the page.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-07-30 04:15:16 +00:00
|
|
|
|
.. attribute:: InlineModelAdmin.verbose_name
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2010-07-30 04:15:16 +00:00
|
|
|
|
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``.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2014-07-25 12:07:04 +00:00
|
|
|
|
.. attribute:: InlineModelAdmin.show_change_link
|
|
|
|
|
|
|
|
|
|
Specifies whether or not inline objects that can be changed in the
|
|
|
|
|
admin have a link to the change form. Defaults to ``False``.
|
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: InlineModelAdmin.get_formset(request, obj=None, **kwargs)
|
2012-11-03 09:22:34 +00:00
|
|
|
|
|
2013-07-08 12:29:28 +00:00
|
|
|
|
Returns a :class:`~django.forms.models.BaseInlineFormSet` class for use in
|
|
|
|
|
admin add/change views. See the example for
|
2014-03-19 21:28:52 +00:00
|
|
|
|
:class:`ModelAdmin.get_formsets_with_inlines`.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: InlineModelAdmin.get_extra(request, obj=None, **kwargs)
|
2013-05-30 17:48:10 +00:00
|
|
|
|
|
|
|
|
|
Returns the number of extra inline forms to use. By default, returns the
|
|
|
|
|
:attr:`InlineModelAdmin.extra` attribute.
|
|
|
|
|
|
|
|
|
|
Override this method to programmatically determine the number of extra
|
|
|
|
|
inline forms. For example, this may be based on the model instance
|
|
|
|
|
(passed as the keyword argument ``obj``)::
|
|
|
|
|
|
|
|
|
|
class BinaryTreeAdmin(admin.TabularInline):
|
|
|
|
|
model = BinaryTree
|
|
|
|
|
|
|
|
|
|
def get_extra(self, request, obj=None, **kwargs):
|
|
|
|
|
extra = 2
|
|
|
|
|
if obj:
|
|
|
|
|
return extra - obj.binarytree_set.count()
|
|
|
|
|
return extra
|
|
|
|
|
|
2014-01-22 21:17:32 +00:00
|
|
|
|
.. method:: InlineModelAdmin.get_max_num(request, obj=None, **kwargs)
|
2013-06-01 22:16:57 +00:00
|
|
|
|
|
|
|
|
|
Returns the maximum number of extra inline forms to use. By default,
|
|
|
|
|
returns the :attr:`InlineModelAdmin.max_num` attribute.
|
|
|
|
|
|
|
|
|
|
Override this method to programmatically determine the maximum number of
|
|
|
|
|
inline forms. For example, this may be based on the model instance
|
|
|
|
|
(passed as the keyword argument ``obj``)::
|
|
|
|
|
|
|
|
|
|
class BinaryTreeAdmin(admin.TabularInline):
|
|
|
|
|
model = BinaryTree
|
|
|
|
|
|
|
|
|
|
def get_max_num(self, request, obj=None, **kwargs):
|
|
|
|
|
max_num = 10
|
2016-10-17 11:33:38 +00:00
|
|
|
|
if obj and obj.parent:
|
2013-06-01 22:16:57 +00:00
|
|
|
|
return max_num - 5
|
|
|
|
|
return max_num
|
|
|
|
|
|
2014-03-05 20:19:40 +00:00
|
|
|
|
.. method:: InlineModelAdmin.get_min_num(request, obj=None, **kwargs)
|
|
|
|
|
|
|
|
|
|
Returns the minimum number of inline forms to use. By default,
|
|
|
|
|
returns the :attr:`InlineModelAdmin.min_num` attribute.
|
|
|
|
|
|
|
|
|
|
Override this method to programmatically determine the minimum number of
|
|
|
|
|
inline forms. For example, this may be based on the model instance
|
|
|
|
|
(passed as the keyword argument ``obj``).
|
2013-06-01 22:16:57 +00:00
|
|
|
|
|
2018-02-22 03:11:50 +00:00
|
|
|
|
.. method:: InlineModelAdmin.has_add_permission(request, obj)
|
2018-04-02 21:14:16 +00:00
|
|
|
|
|
|
|
|
|
Should return ``True`` if adding an inline object is permitted, ``False``
|
2018-02-22 03:11:50 +00:00
|
|
|
|
otherwise. ``obj`` is the parent object being edited or ``None`` when
|
|
|
|
|
adding a new parent.
|
|
|
|
|
|
|
|
|
|
.. versionchanged:: 2.1
|
|
|
|
|
|
|
|
|
|
The ``obj`` argument was added.
|
2018-04-02 21:14:16 +00:00
|
|
|
|
|
|
|
|
|
.. method:: InlineModelAdmin.has_change_permission(request, obj=None)
|
|
|
|
|
|
|
|
|
|
Should return ``True`` if editing an inline object is permitted, ``False``
|
|
|
|
|
otherwise. ``obj`` is the parent object being edited.
|
|
|
|
|
|
|
|
|
|
.. method:: InlineModelAdmin.has_delete_permission(request, obj=None)
|
|
|
|
|
|
|
|
|
|
Should return ``True`` if deleting an inline object is permitted, ``False``
|
|
|
|
|
otherwise. ``obj`` is the parent object being edited.
|
|
|
|
|
|
2008-07-18 23:54:34 +00:00
|
|
|
|
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::
|
|
|
|
|
|
2013-05-19 10:42:35 +00:00
|
|
|
|
from django.db import models
|
|
|
|
|
|
2008-07-18 23:54:34 +00:00
|
|
|
|
class Friendship(models.Model):
|
2015-07-22 14:43:21 +00:00
|
|
|
|
to_person = models.ForeignKey(Person, on_delete=models.CASCADE, related_name="friends")
|
|
|
|
|
from_person = models.ForeignKey(Person, on_delete=models.CASCADE, related_name="from_friends")
|
2008-07-27 18:36:08 +00:00
|
|
|
|
|
2008-07-18 23:54:34 +00:00
|
|
|
|
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::
|
|
|
|
|
|
2013-05-19 10:42:35 +00:00
|
|
|
|
from django.contrib import admin
|
|
|
|
|
from myapp.models import Friendship
|
|
|
|
|
|
2008-07-18 23:54:34 +00:00
|
|
|
|
class FriendshipInline(admin.TabularInline):
|
|
|
|
|
model = Friendship
|
|
|
|
|
fk_name = "to_person"
|
2008-07-27 18:36:08 +00:00
|
|
|
|
|
2008-07-18 23:54:34 +00:00
|
|
|
|
class PersonAdmin(admin.ModelAdmin):
|
|
|
|
|
inlines = [
|
|
|
|
|
FriendshipInline,
|
|
|
|
|
]
|
|
|
|
|
|
2011-01-08 21:15:00 +00:00
|
|
|
|
Working with many-to-many models
|
2009-11-03 15:02:16 +00:00
|
|
|
|
--------------------------------
|
|
|
|
|
|
|
|
|
|
By default, admin widgets for many-to-many relations will be displayed
|
2011-01-08 21:15:00 +00:00
|
|
|
|
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
|
2011-02-14 15:02:02 +00:00
|
|
|
|
``raw_id_admin`` widget. However, it is also possible to replace these
|
2011-01-08 21:15:00 +00:00
|
|
|
|
widgets with inlines.
|
2009-11-03 15:02:16 +00:00
|
|
|
|
|
|
|
|
|
Suppose we have the following models::
|
|
|
|
|
|
2013-05-19 10:42:35 +00:00
|
|
|
|
from django.db import models
|
|
|
|
|
|
2009-11-03 15:02:16 +00:00
|
|
|
|
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
|
2009-11-04 15:05:49 +00:00
|
|
|
|
so by defining an ``InlineModelAdmin`` object for the relationship::
|
2009-11-03 15:02:16 +00:00
|
|
|
|
|
2013-05-19 10:42:35 +00:00
|
|
|
|
from django.contrib import admin
|
|
|
|
|
|
2009-11-03 15:02:16 +00:00
|
|
|
|
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.
|
|
|
|
|
|
2015-01-29 18:17:16 +00:00
|
|
|
|
Note that when using this technique the
|
|
|
|
|
:data:`~django.db.models.signals.m2m_changed` signals aren't triggered. This
|
|
|
|
|
is because as far as the admin is concerned, ``through`` is just a model with
|
|
|
|
|
two foreign key fields rather than a many-to-many relation.
|
|
|
|
|
|
2009-11-03 15:02:16 +00:00
|
|
|
|
In all other respects, the ``InlineModelAdmin`` is exactly the same as any
|
|
|
|
|
other. You can customize the appearance using any of the normal
|
2010-05-28 15:03:12 +00:00
|
|
|
|
``ModelAdmin`` properties.
|
2009-11-03 15:02:16 +00:00
|
|
|
|
|
2011-01-08 21:15:00 +00:00
|
|
|
|
Working with many-to-many intermediary models
|
|
|
|
|
---------------------------------------------
|
2008-07-29 12:41:08 +00:00
|
|
|
|
|
2009-11-03 15:02:16 +00:00
|
|
|
|
When you specify an intermediary model using the ``through`` argument to a
|
2011-01-08 21:15:00 +00:00
|
|
|
|
: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.
|
2008-07-29 12:41:08 +00:00
|
|
|
|
|
|
|
|
|
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::
|
|
|
|
|
|
2013-05-19 10:42:35 +00:00
|
|
|
|
from django.db import models
|
|
|
|
|
|
2008-07-29 12:41:08 +00:00
|
|
|
|
class Person(models.Model):
|
|
|
|
|
name = models.CharField(max_length=128)
|
2009-02-22 06:08:13 +00:00
|
|
|
|
|
2008-07-29 12:41:08 +00:00
|
|
|
|
class Group(models.Model):
|
|
|
|
|
name = models.CharField(max_length=128)
|
|
|
|
|
members = models.ManyToManyField(Person, through='Membership')
|
|
|
|
|
|
|
|
|
|
class Membership(models.Model):
|
2015-07-22 14:43:21 +00:00
|
|
|
|
person = models.ForeignKey(Person, on_delete=models.CASCADE)
|
|
|
|
|
group = models.ForeignKey(Group, on_delete=models.CASCADE)
|
2008-07-29 12:41:08 +00:00
|
|
|
|
date_joined = models.DateField()
|
|
|
|
|
invite_reason = models.CharField(max_length=64)
|
|
|
|
|
|
|
|
|
|
The first step in displaying this intermediate model in the admin is to
|
2008-08-04 19:37:47 +00:00
|
|
|
|
define an inline class for the ``Membership`` model::
|
2008-07-29 12:41:08 +00:00
|
|
|
|
|
|
|
|
|
class MembershipInline(admin.TabularInline):
|
|
|
|
|
model = Membership
|
|
|
|
|
extra = 1
|
|
|
|
|
|
2008-08-04 19:37:47 +00:00
|
|
|
|
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.
|
2008-07-29 12:41:08 +00:00
|
|
|
|
|
|
|
|
|
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::
|
2009-02-22 06:08:13 +00:00
|
|
|
|
|
2008-07-29 12:41:08 +00:00
|
|
|
|
admin.site.register(Person, PersonAdmin)
|
|
|
|
|
admin.site.register(Group, GroupAdmin)
|
|
|
|
|
|
2008-08-04 19:37:47 +00:00
|
|
|
|
Now your admin site is set up to edit ``Membership`` objects inline from
|
|
|
|
|
either the ``Person`` or the ``Group`` detail pages.
|
2008-07-29 12:41:08 +00:00
|
|
|
|
|
2010-12-31 23:02:07 +00:00
|
|
|
|
.. _using-generic-relations-as-an-inline:
|
|
|
|
|
|
2008-08-10 04:03:01 +00:00
|
|
|
|
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::
|
|
|
|
|
|
2014-01-22 06:43:33 +00:00
|
|
|
|
from django.contrib.contenttypes.fields import GenericForeignKey
|
2018-05-12 17:37:42 +00:00
|
|
|
|
from django.db import models
|
2013-05-19 10:42:35 +00:00
|
|
|
|
|
2008-08-10 04:03:01 +00:00
|
|
|
|
class Image(models.Model):
|
|
|
|
|
image = models.ImageField(upload_to="images")
|
2015-07-22 14:43:21 +00:00
|
|
|
|
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
|
2008-08-10 04:03:01 +00:00
|
|
|
|
object_id = models.PositiveIntegerField()
|
2014-01-22 06:43:33 +00:00
|
|
|
|
content_object = GenericForeignKey("content_type", "object_id")
|
2009-02-22 06:08:13 +00:00
|
|
|
|
|
2008-08-10 04:03:01 +00:00
|
|
|
|
class Product(models.Model):
|
|
|
|
|
name = models.CharField(max_length=100)
|
|
|
|
|
|
2015-05-21 15:07:38 +00:00
|
|
|
|
If you want to allow editing and creating an ``Image`` instance on the
|
|
|
|
|
``Product``, add/change views you can use
|
|
|
|
|
:class:`~django.contrib.contenttypes.admin.GenericTabularInline`
|
2014-01-22 06:43:33 +00:00
|
|
|
|
or :class:`~django.contrib.contenttypes.admin.GenericStackedInline` (both
|
|
|
|
|
subclasses of :class:`~django.contrib.contenttypes.admin.GenericInlineModelAdmin`)
|
2015-05-21 15:07:38 +00:00
|
|
|
|
provided by :mod:`~django.contrib.contenttypes.admin`. They implement tabular
|
|
|
|
|
and stacked visual layouts for the forms representing the inline objects,
|
|
|
|
|
respectively, just like their non-generic counterparts. They behave just like
|
|
|
|
|
any other inline. In your ``admin.py`` for this example app::
|
2008-08-10 04:03:01 +00:00
|
|
|
|
|
|
|
|
|
from django.contrib import admin
|
2014-01-22 06:43:33 +00:00
|
|
|
|
from django.contrib.contenttypes.admin import GenericTabularInline
|
2009-02-22 06:08:13 +00:00
|
|
|
|
|
2008-08-10 04:03:01 +00:00
|
|
|
|
from myproject.myapp.models import Image, Product
|
2009-02-22 06:08:13 +00:00
|
|
|
|
|
2014-01-22 06:43:33 +00:00
|
|
|
|
class ImageInline(GenericTabularInline):
|
2008-08-10 04:03:01 +00:00
|
|
|
|
model = Image
|
2009-02-22 06:08:13 +00:00
|
|
|
|
|
2008-08-10 04:03:01 +00:00
|
|
|
|
class ProductAdmin(admin.ModelAdmin):
|
|
|
|
|
inlines = [
|
|
|
|
|
ImageInline,
|
|
|
|
|
]
|
2009-02-22 06:08:13 +00:00
|
|
|
|
|
2008-08-10 04:03:01 +00:00
|
|
|
|
admin.site.register(Product, ProductAdmin)
|
|
|
|
|
|
2010-12-31 23:02:07 +00:00
|
|
|
|
See the :doc:`contenttypes documentation </ref/contrib/contenttypes>` for more
|
|
|
|
|
specific information.
|
2008-08-10 04:03:01 +00:00
|
|
|
|
|
2014-10-06 12:59:04 +00:00
|
|
|
|
.. _admin-overriding-templates:
|
|
|
|
|
|
2011-01-08 21:15:00 +00:00
|
|
|
|
Overriding admin templates
|
2008-09-02 16:47:45 +00:00
|
|
|
|
==========================
|
|
|
|
|
|
|
|
|
|
It is relatively easy to override many of the templates which the admin module
|
2010-12-05 04:52:31 +00:00
|
|
|
|
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.
|
2008-09-02 16:47:45 +00:00
|
|
|
|
|
|
|
|
|
Set up your projects admin template directories
|
|
|
|
|
-----------------------------------------------
|
|
|
|
|
|
|
|
|
|
The admin template files are located in the ``contrib/admin/templates/admin``
|
|
|
|
|
directory.
|
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
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
|
2014-12-17 21:10:57 +00:00
|
|
|
|
you specified in the :setting:`DIRS <TEMPLATES-DIRS>` option of the
|
|
|
|
|
``DjangoTemplates`` backend in the :setting:`TEMPLATES` setting. If you have
|
|
|
|
|
customized the ``'loaders'`` option, be sure
|
2014-02-23 14:10:28 +00:00
|
|
|
|
``'django.template.loaders.filesystem.Loader'`` appears before
|
|
|
|
|
``'django.template.loaders.app_directories.Loader'`` so that your custom
|
|
|
|
|
templates will be found by the template loading system before those that are
|
|
|
|
|
included with :mod:`django.contrib.admin`.
|
2008-09-02 16:47:45 +00:00
|
|
|
|
|
|
|
|
|
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
|
2010-12-05 04:52:31 +00:00
|
|
|
|
directory, so make sure you name the directory in all lowercase if you are
|
|
|
|
|
going to run your app on a case-sensitive filesystem.
|
2008-09-02 16:47:45 +00:00
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
2013-01-02 22:32:10 +00:00
|
|
|
|
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-items`` block.
|
|
|
|
|
Therefore here is our new ``change_form.html`` :
|
2008-09-09 01:54:20 +00:00
|
|
|
|
|
|
|
|
|
.. code-block:: html+django
|
2008-09-02 16:47:45 +00:00
|
|
|
|
|
|
|
|
|
{% extends "admin/change_form.html" %}
|
2013-01-02 22:32:10 +00:00
|
|
|
|
{% load i18n admin_urls %}
|
|
|
|
|
{% block object-tools-items %}
|
|
|
|
|
<li>
|
|
|
|
|
<a href="{% url opts|admin_urlname:'history' original.pk|admin_urlquote %}" class="historylink">{% trans "History" %}</a>
|
|
|
|
|
</li>
|
|
|
|
|
<li>
|
|
|
|
|
<a href="mylink/" class="historylink">My Link</a>
|
|
|
|
|
</li>
|
2008-09-02 16:47:45 +00:00
|
|
|
|
{% if has_absolute_url %}
|
2013-01-02 22:32:10 +00:00
|
|
|
|
<li>
|
|
|
|
|
<a href="{% url 'admin:view_on_site' content_type_id original.pk %}" class="viewsitelink">{% trans "View on site" %}</a>
|
2008-09-02 16:47:45 +00:00
|
|
|
|
</li>
|
2014-12-12 13:27:04 +00:00
|
|
|
|
{% endif %}
|
2008-09-02 16:47:45 +00:00
|
|
|
|
{% endblock %}
|
|
|
|
|
|
|
|
|
|
And that's it! If we placed this file in the ``templates/admin/my_app``
|
2013-01-02 22:32:10 +00:00
|
|
|
|
directory, our link would appear on the change form for all models within
|
|
|
|
|
my_app.
|
2008-09-02 16:47:45 +00:00
|
|
|
|
|
2018-02-28 11:55:52 +00:00
|
|
|
|
.. _admin-templates-overridden-per-app-or-model:
|
|
|
|
|
|
2008-09-02 16:47:45 +00:00
|
|
|
|
Templates which may be overridden per app or model
|
|
|
|
|
--------------------------------------------------
|
|
|
|
|
|
2009-06-24 14:02:22 +00:00
|
|
|
|
Not every template in ``contrib/admin/templates/admin`` may be overridden per
|
2008-09-02 16:47:45 +00:00
|
|
|
|
app or per model. The following can:
|
|
|
|
|
|
2017-01-12 16:06:00 +00:00
|
|
|
|
* ``actions.html``
|
2011-10-10 17:32:33 +00:00
|
|
|
|
* ``app_index.html``
|
|
|
|
|
* ``change_form.html``
|
2017-01-12 16:06:00 +00:00
|
|
|
|
* ``change_form_object_tools.html``
|
2011-10-10 17:32:33 +00:00
|
|
|
|
* ``change_list.html``
|
2017-01-12 16:06:00 +00:00
|
|
|
|
* ``change_list_object_tools.html``
|
|
|
|
|
* ``change_list_results.html``
|
|
|
|
|
* ``date_hierarchy.html``
|
2011-10-10 17:32:33 +00:00
|
|
|
|
* ``delete_confirmation.html``
|
|
|
|
|
* ``object_history.html``
|
2017-01-12 16:06:00 +00:00
|
|
|
|
* ``pagination.html``
|
2016-11-12 19:09:15 +00:00
|
|
|
|
* ``popup_response.html``
|
2017-01-12 16:06:00 +00:00
|
|
|
|
* ``prepopulated_fields_js.html``
|
|
|
|
|
* ``search_form.html``
|
|
|
|
|
* ``submit_line.html``
|
|
|
|
|
|
|
|
|
|
.. versionchanged:: 2.1
|
|
|
|
|
|
|
|
|
|
The ability to override the ``actions.html``, ``change_form_object_tools.html``,
|
|
|
|
|
``change_list_object_tools.html``, ``change_list_results.html``,
|
|
|
|
|
``date_hierarchy.html``, ``pagination.html``, ``prepopulated_fields_js.html``,
|
2018-02-28 11:55:52 +00:00
|
|
|
|
``search_form.html``, and ``submit_line.html`` templates was added.
|
2016-11-12 19:09:15 +00:00
|
|
|
|
|
2008-09-02 16:47:45 +00:00
|
|
|
|
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::
|
|
|
|
|
|
2013-03-21 10:03:28 +00:00
|
|
|
|
Some of the admin templates, such as ``change_list_results.html`` are used
|
2008-09-02 16:47:45 +00:00
|
|
|
|
to render custom inclusion tags. These may be overridden, but in such cases
|
2010-12-05 04:52:31 +00:00
|
|
|
|
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.
|
2008-09-02 16:47:45 +00:00
|
|
|
|
|
|
|
|
|
Root and login templates
|
|
|
|
|
------------------------
|
|
|
|
|
|
2010-01-12 23:34:46 +00:00
|
|
|
|
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.
|
2008-09-02 16:47:45 +00:00
|
|
|
|
|
2008-07-18 23:54:34 +00:00
|
|
|
|
``AdminSite`` objects
|
|
|
|
|
=====================
|
|
|
|
|
|
2011-09-03 17:46:16 +00:00
|
|
|
|
.. class:: AdminSite(name='admin')
|
2009-04-16 12:47:34 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
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.
|
2008-08-30 05:29:19 +00:00
|
|
|
|
|
2016-12-29 17:51:26 +00:00
|
|
|
|
If you want to customize the default admin site, you can :ref:`override it
|
|
|
|
|
<overriding-default-admin-site>`.
|
|
|
|
|
|
2013-09-06 17:31:44 +00:00
|
|
|
|
When constructing an instance of an ``AdminSite``, you can provide
|
2010-12-05 04:52:31 +00:00
|
|
|
|
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.
|
2014-10-30 11:57:22 +00:00
|
|
|
|
See :ref:`customizing-adminsite` for an example of customizing the
|
|
|
|
|
:class:`AdminSite` class.
|
2009-07-16 16:16:13 +00:00
|
|
|
|
|
2009-06-24 14:02:22 +00:00
|
|
|
|
``AdminSite`` attributes
|
|
|
|
|
------------------------
|
|
|
|
|
|
2010-01-12 23:34:46 +00:00
|
|
|
|
Templates can override or extend base admin templates as described in
|
2014-10-06 12:59:04 +00:00
|
|
|
|
:ref:`admin-overriding-templates`.
|
2010-01-12 23:34:46 +00:00
|
|
|
|
|
2013-09-06 17:31:44 +00:00
|
|
|
|
.. attribute:: AdminSite.site_header
|
2013-09-06 20:15:35 +00:00
|
|
|
|
|
2013-09-06 17:31:44 +00:00
|
|
|
|
The text to put at the top of each admin page, as an ``<h1>`` (a string).
|
|
|
|
|
By default, this is "Django administration".
|
|
|
|
|
|
|
|
|
|
.. attribute:: AdminSite.site_title
|
2013-09-06 20:15:35 +00:00
|
|
|
|
|
2013-09-06 17:31:44 +00:00
|
|
|
|
The text to put at the end of each admin page's ``<title>`` (a string). By
|
|
|
|
|
default, this is "Django site admin".
|
|
|
|
|
|
2014-08-26 11:10:30 +00:00
|
|
|
|
.. attribute:: AdminSite.site_url
|
|
|
|
|
|
|
|
|
|
The URL for the "View site" link at the top of each admin page. By default,
|
|
|
|
|
``site_url`` is ``/``. Set it to ``None`` to remove the link.
|
|
|
|
|
|
2015-10-13 06:52:49 +00:00
|
|
|
|
For sites running on a subpath, the :meth:`each_context` method checks if
|
|
|
|
|
the current request has ``request.META['SCRIPT_NAME']`` set and uses that
|
|
|
|
|
value if ``site_url`` isn't set to something other than ``/``.
|
|
|
|
|
|
2013-09-06 17:31:44 +00:00
|
|
|
|
.. attribute:: AdminSite.index_title
|
2013-09-06 20:15:35 +00:00
|
|
|
|
|
2013-09-06 17:31:44 +00:00
|
|
|
|
The text to put at the top of the admin index page (a string). By default,
|
|
|
|
|
this is "Site administration".
|
|
|
|
|
|
2009-06-24 14:02:22 +00:00
|
|
|
|
.. attribute:: AdminSite.index_template
|
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
Path to a custom template that will be used by the admin site main index
|
|
|
|
|
view.
|
2009-06-24 14:02:22 +00:00
|
|
|
|
|
2013-07-30 09:11:15 +00:00
|
|
|
|
.. attribute:: AdminSite.app_index_template
|
|
|
|
|
|
|
|
|
|
Path to a custom template that will be used by the admin site app index view.
|
|
|
|
|
|
2015-03-13 10:08:03 +00:00
|
|
|
|
.. attribute:: AdminSite.empty_value_display
|
|
|
|
|
|
|
|
|
|
The string to use for displaying empty values in the admin site's change
|
|
|
|
|
list. Defaults to a dash. The value can also be overridden on a per
|
|
|
|
|
``ModelAdmin`` basis and on a custom field within a ``ModelAdmin`` by
|
|
|
|
|
setting an ``empty_value_display`` attribute on the field. See
|
|
|
|
|
:attr:`ModelAdmin.empty_value_display` for examples.
|
|
|
|
|
|
2009-06-24 14:02:22 +00:00
|
|
|
|
.. attribute:: AdminSite.login_template
|
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
Path to a custom template that will be used by the admin site login view.
|
2010-01-12 23:34:46 +00:00
|
|
|
|
|
2010-12-02 00:44:35 +00:00
|
|
|
|
.. attribute:: AdminSite.login_form
|
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
Subclass of :class:`~django.contrib.auth.forms.AuthenticationForm` that
|
|
|
|
|
will be used by the admin site login view.
|
2010-12-02 00:44:35 +00:00
|
|
|
|
|
2010-01-12 23:34:46 +00:00
|
|
|
|
.. attribute:: AdminSite.logout_template
|
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
Path to a custom template that will be used by the admin site logout view.
|
2010-01-12 23:34:46 +00:00
|
|
|
|
|
|
|
|
|
.. attribute:: AdminSite.password_change_template
|
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
Path to a custom template that will be used by the admin site password
|
|
|
|
|
change view.
|
2010-01-12 23:34:46 +00:00
|
|
|
|
|
|
|
|
|
.. attribute:: AdminSite.password_change_done_template
|
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
Path to a custom template that will be used by the admin site password
|
|
|
|
|
change done view.
|
2009-06-24 14:02:22 +00:00
|
|
|
|
|
2014-12-26 14:14:10 +00:00
|
|
|
|
``AdminSite`` methods
|
|
|
|
|
---------------------
|
|
|
|
|
|
2015-01-01 13:18:39 +00:00
|
|
|
|
.. method:: AdminSite.each_context(request)
|
2014-12-31 21:25:00 +00:00
|
|
|
|
|
|
|
|
|
Returns a dictionary of variables to put in the template context for
|
|
|
|
|
every page in the admin site.
|
|
|
|
|
|
|
|
|
|
Includes the following variables and values by default:
|
|
|
|
|
|
|
|
|
|
* ``site_header``: :attr:`AdminSite.site_header`
|
|
|
|
|
* ``site_title``: :attr:`AdminSite.site_title`
|
|
|
|
|
* ``site_url``: :attr:`AdminSite.site_url`
|
|
|
|
|
* ``has_permission``: :meth:`AdminSite.has_permission`
|
2015-03-30 10:54:25 +00:00
|
|
|
|
* ``available_apps``: a list of applications from the :doc:`application registry
|
|
|
|
|
</ref/applications/>` available for the current user. Each entry in the
|
|
|
|
|
list is a dict representing an application with the following keys:
|
|
|
|
|
|
|
|
|
|
* ``app_label``: the application label
|
|
|
|
|
* ``app_url``: the URL of the application index in the admin
|
|
|
|
|
* ``has_module_perms``: a boolean indicating if displaying and accessing of
|
|
|
|
|
the module's index page is permitted for the current user
|
|
|
|
|
* ``models``: a list of the models available in the application
|
|
|
|
|
|
|
|
|
|
Each model is a dict with the following keys:
|
|
|
|
|
|
|
|
|
|
* ``object_name``: class name of the model
|
|
|
|
|
* ``name``: plural name of the model
|
2018-05-02 08:39:12 +00:00
|
|
|
|
* ``perms``: a ``dict`` tracking ``add``, ``change``, ``delete``, and
|
|
|
|
|
``view`` permissions
|
2015-03-30 10:54:25 +00:00
|
|
|
|
* ``admin_url``: admin changelist URL for the model
|
|
|
|
|
* ``add_url``: admin URL to add a new model instance
|
2014-12-31 21:25:00 +00:00
|
|
|
|
|
2014-12-26 14:14:10 +00:00
|
|
|
|
.. method:: AdminSite.has_permission(request)
|
|
|
|
|
|
|
|
|
|
Returns ``True`` if the user for the given ``HttpRequest`` has permission
|
|
|
|
|
to view at least one page in the admin site. Defaults to requiring both
|
|
|
|
|
:attr:`User.is_active <django.contrib.auth.models.User.is_active>` and
|
|
|
|
|
:attr:`User.is_staff <django.contrib.auth.models.User.is_staff>` to be
|
|
|
|
|
``True``.
|
|
|
|
|
|
2016-09-20 03:29:57 +00:00
|
|
|
|
.. method:: AdminSite.register(model_or_iterable, admin_class=None, **options)
|
|
|
|
|
|
|
|
|
|
Registers the given model class (or iterable of classes) with the given
|
|
|
|
|
``admin_class``. ``admin_class`` defaults to
|
|
|
|
|
:class:`~django.contrib.admin.ModelAdmin` (the default admin options). If
|
|
|
|
|
keyword arguments are given -- e.g. ``list_display`` -- they'll be applied
|
|
|
|
|
as options to the admin class.
|
|
|
|
|
|
|
|
|
|
Raises :class:`~django.core.exceptions.ImproperlyConfigured` if a model is
|
|
|
|
|
abstract. and ``django.contrib.admin.sites.AlreadyRegistered`` if a model
|
|
|
|
|
is already registered.
|
|
|
|
|
|
2008-07-18 23:54:34 +00:00
|
|
|
|
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
|
2015-05-28 15:25:52 +00:00
|
|
|
|
``AdminSite.urls`` method. It is not necessary to use
|
2016-10-20 17:29:04 +00:00
|
|
|
|
:func:`~django.urls.include()`.
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
|
|
|
|
In this example, we register the default ``AdminSite`` instance
|
|
|
|
|
``django.contrib.admin.site`` at the URL ``/admin/`` ::
|
|
|
|
|
|
|
|
|
|
# urls.py
|
|
|
|
|
from django.contrib import admin
|
2016-10-20 17:29:04 +00:00
|
|
|
|
from django.urls import path
|
2008-07-27 18:36:08 +00:00
|
|
|
|
|
2014-04-02 00:46:34 +00:00
|
|
|
|
urlpatterns = [
|
2016-10-20 17:29:04 +00:00
|
|
|
|
path('admin/', admin.site.urls),
|
2014-04-02 00:46:34 +00:00
|
|
|
|
]
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2014-10-30 11:57:22 +00:00
|
|
|
|
.. _customizing-adminsite:
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2014-10-30 11:57:22 +00:00
|
|
|
|
Customizing the :class:`AdminSite` class
|
|
|
|
|
----------------------------------------
|
|
|
|
|
|
|
|
|
|
If you'd like to set up your own admin site with custom behavior, 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 with the default site. Finally,
|
|
|
|
|
update :file:`myproject/urls.py` to reference your :class:`AdminSite` subclass.
|
|
|
|
|
|
|
|
|
|
.. snippet::
|
|
|
|
|
:filename: myapp/admin.py
|
|
|
|
|
|
|
|
|
|
from django.contrib.admin import AdminSite
|
|
|
|
|
|
|
|
|
|
from .models import MyModel
|
|
|
|
|
|
|
|
|
|
class MyAdminSite(AdminSite):
|
|
|
|
|
site_header = 'Monty Python administration'
|
|
|
|
|
|
|
|
|
|
admin_site = MyAdminSite(name='myadmin')
|
|
|
|
|
admin_site.register(MyModel)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.. snippet::
|
|
|
|
|
:filename: myproject/urls.py
|
|
|
|
|
|
2016-10-20 17:29:04 +00:00
|
|
|
|
from django.urls import path
|
2014-10-30 12:53:20 +00:00
|
|
|
|
|
2014-10-30 11:57:22 +00:00
|
|
|
|
from myapp.admin import admin_site
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2014-04-02 00:46:34 +00:00
|
|
|
|
urlpatterns = [
|
2016-10-20 17:29:04 +00:00
|
|
|
|
path('myadmin/', admin_site.urls),
|
2014-04-02 00:46:34 +00:00
|
|
|
|
]
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2014-01-24 21:43:00 +00:00
|
|
|
|
Note that you may not want autodiscovery of ``admin`` modules when using your
|
2014-01-18 18:34:54 +00:00
|
|
|
|
own ``AdminSite`` instance since you will likely be importing all the per-app
|
2014-01-24 21:43:00 +00:00
|
|
|
|
``admin`` modules in your ``myproject.admin`` module. This means you need to
|
2014-07-29 16:12:35 +00:00
|
|
|
|
put ``'django.contrib.admin.apps.SimpleAdminConfig'`` instead of
|
2014-01-24 21:43:00 +00:00
|
|
|
|
``'django.contrib.admin'`` in your :setting:`INSTALLED_APPS` setting.
|
|
|
|
|
|
2016-12-29 17:51:26 +00:00
|
|
|
|
.. _overriding-default-admin-site:
|
|
|
|
|
|
|
|
|
|
Overriding the default admin site
|
|
|
|
|
---------------------------------
|
|
|
|
|
|
|
|
|
|
.. versionadded:: 2.1
|
|
|
|
|
|
|
|
|
|
You can override the default ``django.contrib.admin.site`` by setting the
|
|
|
|
|
:attr:`~.SimpleAdminConfig.default_site` attribute of a custom ``AppConfig``
|
|
|
|
|
to the dotted import path of either a ``AdminSite`` subclass or a callable that
|
|
|
|
|
returns a site instance.
|
|
|
|
|
|
|
|
|
|
.. snippet::
|
|
|
|
|
:filename: myproject/admin.py
|
|
|
|
|
|
|
|
|
|
from django.contrib import admin
|
|
|
|
|
|
|
|
|
|
class MyAdminSite(admin.AdminSite):
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
.. snippet::
|
|
|
|
|
:filename: myproject/apps.py
|
|
|
|
|
|
|
|
|
|
from django.contrib.admin.apps import AdminConfig
|
|
|
|
|
|
|
|
|
|
class MyAdminConfig(AdminConfig):
|
|
|
|
|
default_site = 'myproject.admin.MyAdminSite'
|
|
|
|
|
|
|
|
|
|
.. snippet::
|
|
|
|
|
:filename: myproject/settings.py
|
|
|
|
|
|
|
|
|
|
INSTALLED_APPS = [
|
|
|
|
|
...
|
|
|
|
|
'myproject.apps.MyAdminConfig', # replaces 'django.contrib.admin'
|
|
|
|
|
...
|
|
|
|
|
]
|
|
|
|
|
|
2014-08-20 14:12:25 +00:00
|
|
|
|
.. _multiple-admin-sites:
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
|
|
|
|
Multiple admin sites in the same URLconf
|
|
|
|
|
----------------------------------------
|
|
|
|
|
|
|
|
|
|
It's easy to create multiple instances of the admin site on the same
|
2015-11-15 12:05:15 +00:00
|
|
|
|
Django-powered website. Just create multiple instances of ``AdminSite`` and
|
2008-07-18 23:54:34 +00:00
|
|
|
|
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
|
2016-10-20 17:29:04 +00:00
|
|
|
|
from django.urls import path
|
2018-05-12 17:37:42 +00:00
|
|
|
|
from myproject.admin import advanced_site, basic_site
|
2008-07-18 23:54:34 +00:00
|
|
|
|
|
2014-04-02 00:46:34 +00:00
|
|
|
|
urlpatterns = [
|
2016-10-20 17:29:04 +00:00
|
|
|
|
path('basic-admin/', basic_site.urls),
|
|
|
|
|
path('advanced-admin/', advanced_site.urls),
|
2014-04-02 00:46:34 +00:00
|
|
|
|
]
|
2009-01-14 20:22:25 +00:00
|
|
|
|
|
2009-04-16 12:47:34 +00:00
|
|
|
|
``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``.
|
|
|
|
|
|
2009-01-14 20:22:25 +00:00
|
|
|
|
Adding views to admin sites
|
|
|
|
|
---------------------------
|
|
|
|
|
|
2009-04-16 12:47:34 +00:00
|
|
|
|
Just like :class:`ModelAdmin`, :class:`AdminSite` provides a
|
2009-04-16 12:46:58 +00:00
|
|
|
|
: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.
|
2009-04-16 12:47:34 +00:00
|
|
|
|
|
|
|
|
|
.. note::
|
2011-09-16 18:06:42 +00:00
|
|
|
|
|
2009-04-16 12:47:34 +00:00
|
|
|
|
Any view you render that uses the admin templates, or extends the base
|
2014-12-14 16:48:51 +00:00
|
|
|
|
admin template, should set ``request.current_app`` before 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``.
|
|
|
|
|
|
2011-12-30 20:23:26 +00:00
|
|
|
|
.. _auth_password_reset:
|
|
|
|
|
|
2017-01-12 00:01:30 +00:00
|
|
|
|
Adding a password reset feature
|
2011-12-30 20:23:26 +00:00
|
|
|
|
-------------------------------
|
|
|
|
|
|
2017-01-12 00:01:30 +00:00
|
|
|
|
You can add a password reset feature to the admin site by adding a few lines to
|
2014-08-18 14:30:44 +00:00
|
|
|
|
your URLconf. Specifically, add these four patterns::
|
2011-12-30 20:23:26 +00:00
|
|
|
|
|
2014-08-12 14:54:42 +00:00
|
|
|
|
from django.contrib.auth import views as auth_views
|
|
|
|
|
|
2016-10-20 17:29:04 +00:00
|
|
|
|
path(
|
|
|
|
|
'admin/password_reset/',
|
2017-01-12 00:01:30 +00:00
|
|
|
|
auth_views.PasswordResetView.as_view(),
|
|
|
|
|
name='admin_password_reset',
|
|
|
|
|
),
|
2016-10-20 17:29:04 +00:00
|
|
|
|
path(
|
|
|
|
|
'admin/password_reset/done/',
|
2017-01-12 00:01:30 +00:00
|
|
|
|
auth_views.PasswordResetDoneView.as_view(),
|
|
|
|
|
name='password_reset_done',
|
|
|
|
|
),
|
2016-10-20 17:29:04 +00:00
|
|
|
|
path(
|
|
|
|
|
'reset/<uidb64>/<token>/',
|
2017-01-12 00:01:30 +00:00
|
|
|
|
auth_views.PasswordResetConfirmView.as_view(),
|
|
|
|
|
name='password_reset_confirm',
|
|
|
|
|
),
|
2016-10-20 17:29:04 +00:00
|
|
|
|
path(
|
|
|
|
|
'reset/done/',
|
2017-01-12 00:01:30 +00:00
|
|
|
|
auth_views.PasswordResetCompleteView.as_view(),
|
|
|
|
|
name='password_reset_complete',
|
|
|
|
|
),
|
2011-12-30 20:23:26 +00:00
|
|
|
|
|
|
|
|
|
(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).
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
2015-11-02 09:59:22 +00:00
|
|
|
|
``LogEntry`` objects
|
|
|
|
|
====================
|
|
|
|
|
|
|
|
|
|
.. class:: models.LogEntry
|
|
|
|
|
|
|
|
|
|
The ``LogEntry`` class tracks additions, changes, and deletions of objects
|
|
|
|
|
done through the admin interface.
|
|
|
|
|
|
|
|
|
|
.. currentmodule:: django.contrib.admin.models
|
|
|
|
|
|
|
|
|
|
``LogEntry`` attributes
|
|
|
|
|
-----------------------
|
|
|
|
|
|
|
|
|
|
.. attribute:: LogEntry.action_time
|
|
|
|
|
|
|
|
|
|
The date and time of the action.
|
|
|
|
|
|
|
|
|
|
.. attribute:: LogEntry.user
|
|
|
|
|
|
|
|
|
|
The user (an :setting:`AUTH_USER_MODEL` instance) who performed the
|
|
|
|
|
action.
|
|
|
|
|
|
|
|
|
|
.. attribute:: LogEntry.content_type
|
|
|
|
|
|
|
|
|
|
The :class:`~django.contrib.contenttypes.models.ContentType` of the
|
|
|
|
|
modified object.
|
|
|
|
|
|
|
|
|
|
.. attribute:: LogEntry.object_id
|
|
|
|
|
|
|
|
|
|
The textual representation of the modified object's primary key.
|
|
|
|
|
|
|
|
|
|
.. attribute:: LogEntry.object_repr
|
|
|
|
|
|
|
|
|
|
The object`s ``repr()`` after the modification.
|
|
|
|
|
|
|
|
|
|
.. attribute:: LogEntry.action_flag
|
|
|
|
|
|
|
|
|
|
The type of action logged: ``ADDITION``, ``CHANGE``, ``DELETION``.
|
|
|
|
|
|
|
|
|
|
For example, to get a list of all additions done through the admin::
|
|
|
|
|
|
2018-05-12 17:37:42 +00:00
|
|
|
|
from django.contrib.admin.models import ADDITION, LogEntry
|
2015-11-02 09:59:22 +00:00
|
|
|
|
|
|
|
|
|
LogEntry.objects.filter(action_flag=ADDITION)
|
|
|
|
|
|
|
|
|
|
.. attribute:: LogEntry.change_message
|
|
|
|
|
|
|
|
|
|
The detailed description of the modification. In the case of an edit, for
|
2015-12-26 18:51:22 +00:00
|
|
|
|
example, the message contains a list of the edited fields. The Django admin
|
|
|
|
|
site formats this content as a JSON structure, so that
|
|
|
|
|
:meth:`get_change_message` can recompose a message translated in the current
|
|
|
|
|
user language. Custom code might set this as a plain string though. You are
|
|
|
|
|
advised to use the :meth:`get_change_message` method to retrieve this value
|
|
|
|
|
instead of accessing it directly.
|
|
|
|
|
|
2015-11-02 09:59:22 +00:00
|
|
|
|
``LogEntry`` methods
|
|
|
|
|
--------------------
|
|
|
|
|
|
|
|
|
|
.. method:: LogEntry.get_edited_object()
|
|
|
|
|
|
|
|
|
|
A shortcut that returns the referenced object.
|
|
|
|
|
|
2015-12-26 18:51:22 +00:00
|
|
|
|
.. method:: LogEntry.get_change_message()
|
|
|
|
|
|
|
|
|
|
Formats and translates :attr:`change_message` into the current user
|
|
|
|
|
language. Messages created before Django 1.10 will always be displayed in
|
|
|
|
|
the language in which they were logged.
|
|
|
|
|
|
2015-11-02 09:59:22 +00:00
|
|
|
|
.. currentmodule:: django.contrib.admin
|
|
|
|
|
|
2009-04-16 12:47:34 +00:00
|
|
|
|
.. _admin-reverse-urls:
|
|
|
|
|
|
2011-01-08 21:15:00 +00:00
|
|
|
|
Reversing admin URLs
|
2009-04-16 12:47:34 +00:00
|
|
|
|
====================
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
2012-06-08 10:32:16 +00:00
|
|
|
|
========================= ======================== ==================================
|
|
|
|
|
Page URL name Parameters
|
|
|
|
|
========================= ======================== ==================================
|
|
|
|
|
Index ``index``
|
2016-09-26 14:38:34 +00:00
|
|
|
|
Login ``login``
|
2012-06-08 10:32:16 +00:00
|
|
|
|
Logout ``logout``
|
|
|
|
|
Password change ``password_change``
|
|
|
|
|
Password change done ``password_change_done``
|
2015-04-30 19:39:29 +00:00
|
|
|
|
i18n JavaScript ``jsi18n``
|
2012-06-08 10:32:16 +00:00
|
|
|
|
Application index page ``app_list`` ``app_label``
|
|
|
|
|
Redirect to object's page ``view_on_site`` ``content_type_id``, ``object_id``
|
|
|
|
|
========================= ======================== ==================================
|
2009-04-16 12:47:34 +00:00
|
|
|
|
|
|
|
|
|
Each :class:`ModelAdmin` instance provides an additional set of named URLs:
|
|
|
|
|
|
2011-10-10 17:32:33 +00:00
|
|
|
|
====================== =============================================== =============
|
|
|
|
|
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``
|
|
|
|
|
====================== =============================================== =============
|
2009-04-16 12:47:34 +00:00
|
|
|
|
|
2015-11-19 00:33:15 +00:00
|
|
|
|
The ``UserAdmin`` provides a named URL:
|
|
|
|
|
|
|
|
|
|
====================== =============================================== =============
|
|
|
|
|
Page URL name Parameters
|
|
|
|
|
====================== =============================================== =============
|
|
|
|
|
Password change ``auth_user_password_change`` ``user_id``
|
|
|
|
|
====================== =============================================== =============
|
|
|
|
|
|
2009-07-16 16:16:13 +00:00
|
|
|
|
These named URLs are registered with the application namespace ``admin``, and
|
|
|
|
|
with an instance namespace corresponding to the name of the Site instance.
|
2009-04-16 12:47:34 +00:00
|
|
|
|
|
|
|
|
|
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::
|
|
|
|
|
|
2015-12-30 15:51:16 +00:00
|
|
|
|
>>> from django.urls import reverse
|
2009-04-16 12:47:34 +00:00
|
|
|
|
>>> c = Choice.objects.get(...)
|
2015-12-30 15:51:16 +00:00
|
|
|
|
>>> change_url = reverse('admin:polls_choice_change', args=(c.id,))
|
2009-07-16 16:16:13 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
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.
|
2009-07-16 16:16:13 +00:00
|
|
|
|
|
2010-12-05 04:52:31 +00:00
|
|
|
|
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::
|
2009-04-16 12:47:34 +00:00
|
|
|
|
|
2015-12-30 15:51:16 +00:00
|
|
|
|
>>> change_url = reverse('admin:polls_choice_change', args=(c.id,), current_app='custom')
|
2009-04-16 12:47:34 +00:00
|
|
|
|
|
2009-07-16 16:16:13 +00:00
|
|
|
|
For more details, see the documentation on :ref:`reversing namespaced URLs
|
|
|
|
|
<topics-http-reversing-url-namespaces>`.
|
2011-09-20 18:30:06 +00:00
|
|
|
|
|
2011-10-03 08:06:01 +00:00
|
|
|
|
To allow easier reversing of the admin urls in templates, Django provides an
|
|
|
|
|
``admin_urlname`` filter which takes an action as argument:
|
2011-09-20 18:30:06 +00:00
|
|
|
|
|
|
|
|
|
.. code-block:: html+django
|
|
|
|
|
|
|
|
|
|
{% load admin_urls %}
|
|
|
|
|
<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
|
2013-03-29 17:59:34 +00:00
|
|
|
|
object which has an ``app_label`` and ``model_name`` attributes and is usually
|
|
|
|
|
supplied by the admin views for the current model.
|
2015-03-09 18:50:01 +00:00
|
|
|
|
|
|
|
|
|
.. currentmodule:: django.contrib.admin.views.decorators
|
|
|
|
|
|
|
|
|
|
The ``staff_member_required`` decorator
|
|
|
|
|
=======================================
|
|
|
|
|
|
2015-07-27 12:35:21 +00:00
|
|
|
|
.. function:: staff_member_required(redirect_field_name='next', login_url='admin:login')
|
2015-03-09 18:50:01 +00:00
|
|
|
|
|
|
|
|
|
This decorator is used on the admin views that require authorization. A
|
|
|
|
|
view decorated with this function will having the following behavior:
|
|
|
|
|
|
|
|
|
|
* If the user is logged in, is a staff member (``User.is_staff=True``), and
|
|
|
|
|
is active (``User.is_active=True``), execute the view normally.
|
|
|
|
|
|
|
|
|
|
* Otherwise, the request will be redirected to the URL specified by the
|
|
|
|
|
``login_url`` parameter, with the originally requested path in a query
|
|
|
|
|
string variable specified by ``redirect_field_name``. For example:
|
|
|
|
|
``/admin/login/?next=/admin/polls/question/3/``.
|
|
|
|
|
|
|
|
|
|
Example usage::
|
|
|
|
|
|
|
|
|
|
from django.contrib.admin.views.decorators import staff_member_required
|
|
|
|
|
|
|
|
|
|
@staff_member_required
|
|
|
|
|
def my_view(request):
|
|
|
|
|
...
|