summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaffaele Salmaso <raffaele@salmaso.org>2016-12-29 18:51:26 +0100
committerTim Graham <timograham@gmail.com>2018-02-03 18:51:10 -0500
commitda3df5b878d96ef37dcff1377ccbd097b80776d7 (patch)
tree2ded9c48939cfbc3922e3600389872b41ef93b6a
parentd0a42a14c06e033922f6d51e6384cba53be887b6 (diff)
Fixed #8500 -- Allowed overriding the default admin site instance.
-rw-r--r--AUTHORS1
-rw-r--r--django/contrib/admin/apps.py1
-rw-r--r--django/contrib/admin/sites.py14
-rw-r--r--docs/ref/contrib/admin/index.txt49
-rw-r--r--docs/releases/2.1.txt3
-rw-r--r--tests/admin_default_site/__init__.py0
-rw-r--r--tests/admin_default_site/apps.py6
-rw-r--r--tests/admin_default_site/sites.py5
-rw-r--r--tests/admin_default_site/tests.py31
9 files changed, 108 insertions, 2 deletions
diff --git a/AUTHORS b/AUTHORS
index af350240b1..f5eba42531 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -666,6 +666,7 @@ answer newbie questions, and generally made Django that much better:
Rachel Tobin <rmtobin@me.com>
Rachel Willmer <http://www.willmer.com/kb/>
Radek Švarz <http://www.svarz.cz/translate/>
+ Raffaele Salmaso <raffaele@salmaso.org>
Rajesh Dhawan <rajesh.dhawan@gmail.com>
Ramez Ashraf <ramezashraf@gmail.com>
Ramin Farajpour Cami <ramin.blackhat@gmail.com>
diff --git a/django/contrib/admin/apps.py b/django/contrib/admin/apps.py
index df7d669ab0..36c157683d 100644
--- a/django/contrib/admin/apps.py
+++ b/django/contrib/admin/apps.py
@@ -7,6 +7,7 @@ from django.utils.translation import gettext_lazy as _
class SimpleAdminConfig(AppConfig):
"""Simple AppConfig which does not do automatic discovery."""
+ default_site = 'django.contrib.admin.sites.AdminSite'
name = 'django.contrib.admin'
verbose_name = _("Administration")
diff --git a/django/contrib/admin/sites.py b/django/contrib/admin/sites.py
index a3871fb534..f7d0ac0fbc 100644
--- a/django/contrib/admin/sites.py
+++ b/django/contrib/admin/sites.py
@@ -9,6 +9,8 @@ from django.db.models.base import ModelBase
from django.http import Http404, HttpResponseRedirect
from django.template.response import TemplateResponse
from django.urls import NoReverseMatch, reverse
+from django.utils.functional import LazyObject
+from django.utils.module_loading import import_string
from django.utils.text import capfirst
from django.utils.translation import gettext as _, gettext_lazy
from django.views.decorators.cache import never_cache
@@ -518,6 +520,14 @@ class AdminSite:
], context)
+class DefaultAdminSite(LazyObject):
+ def _setup(self):
+ AdminSiteClass = import_string(apps.get_app_config('admin').default_site)
+ self._wrapped = AdminSiteClass()
+
+
# This global object represents the default admin site, for the common case.
-# You can instantiate AdminSite in your own code to create a custom admin site.
-site = AdminSite()
+# You can provide your own AdminSite using the (Simple)AdminConfig.default_site
+# attribute. You can also instantiate AdminSite in your own code to create a
+# custom admin site.
+site = DefaultAdminSite()
diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt
index 39c02f26af..9a2edb698c 100644
--- a/docs/ref/contrib/admin/index.txt
+++ b/docs/ref/contrib/admin/index.txt
@@ -157,6 +157,15 @@ application and imports it.
This class works like :class:`~django.contrib.admin.apps.AdminConfig`,
except it doesn't call :func:`~django.contrib.admin.autodiscover()`.
+ .. 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.
+
.. function:: autodiscover
This function attempts to import an ``admin`` module in each installed
@@ -2627,6 +2636,9 @@ creating your own ``AdminSite`` instance (see below), and changing the
this class is created as ``django.contrib.admin.site`` and you can
register your models and ``ModelAdmin`` instances with it.
+ If you want to customize the default admin site, you can :ref:`override it
+ <overriding-default-admin-site>`.
+
When constructing an instance of an ``AdminSite``, you can provide
a unique instance name using the ``name`` argument to the constructor. This
instance name is used to identify the instance, especially when
@@ -2819,6 +2831,43 @@ own ``AdminSite`` instance since you will likely be importing all the per-app
put ``'django.contrib.admin.apps.SimpleAdminConfig'`` instead of
``'django.contrib.admin'`` in your :setting:`INSTALLED_APPS` setting.
+.. _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'
+ ...
+ ]
+
.. _multiple-admin-sites:
Multiple admin sites in the same URLconf
diff --git a/docs/releases/2.1.txt b/docs/releases/2.1.txt
index f1c3011c9d..ce5fd3551b 100644
--- a/docs/releases/2.1.txt
+++ b/docs/releases/2.1.txt
@@ -40,6 +40,9 @@ Minor features
* The new :meth:`.ModelAdmin.delete_queryset` method allows customizing the
deletion process of the "delete selected objects" action.
+* You can now :ref:`override the the default admin site
+ <overriding-default-admin-site>`.
+
:mod:`django.contrib.admindocs`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/tests/admin_default_site/__init__.py b/tests/admin_default_site/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/admin_default_site/__init__.py
diff --git a/tests/admin_default_site/apps.py b/tests/admin_default_site/apps.py
new file mode 100644
index 0000000000..92743c18d4
--- /dev/null
+++ b/tests/admin_default_site/apps.py
@@ -0,0 +1,6 @@
+from django.contrib.admin.apps import SimpleAdminConfig
+
+
+class MyCustomAdminConfig(SimpleAdminConfig):
+ verbose_name = 'My custom default admin site.'
+ default_site = 'admin_default_site.sites.CustomAdminSite'
diff --git a/tests/admin_default_site/sites.py b/tests/admin_default_site/sites.py
new file mode 100644
index 0000000000..f2c33bd790
--- /dev/null
+++ b/tests/admin_default_site/sites.py
@@ -0,0 +1,5 @@
+from django.contrib import admin
+
+
+class CustomAdminSite(admin.AdminSite):
+ pass
diff --git a/tests/admin_default_site/tests.py b/tests/admin_default_site/tests.py
new file mode 100644
index 0000000000..5d05ec9c45
--- /dev/null
+++ b/tests/admin_default_site/tests.py
@@ -0,0 +1,31 @@
+from django.contrib import admin
+from django.contrib.admin import sites
+from django.test import SimpleTestCase, override_settings
+
+
+@override_settings(INSTALLED_APPS=[
+ 'admin_default_site.apps.MyCustomAdminConfig',
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.messages',
+ 'django.contrib.staticfiles',
+])
+class CustomAdminSiteTests(SimpleTestCase):
+
+ def setUp(self):
+ # Reset admin.site since it may have already been instantiated by
+ # another test app.
+ self._old_site = admin.site
+ admin.site = sites.site = sites.DefaultAdminSite()
+
+ def tearDown(self):
+ admin.site = sites.site = self._old_site
+
+ def test_use_custom_admin_site(self):
+ self.assertEqual(admin.site.__class__.__name__, 'CustomAdminSite')
+
+
+class DefaultAdminSiteTests(SimpleTestCase):
+ def test_use_default_admin_site(self):
+ self.assertEqual(admin.site.__class__.__name__, 'AdminSite')