The Problem
Using a custom class derived from django.contrib.admin.AdminSite
for the admin site of a project, without having to write custom registration code to register models with the new class. When I use 3rd party apps with their own models, I’d rather not have to edit custom registration code only because models were added or removed from these apps.
The Solution
You have to switch the instance created with the default class used for the admin site to your own instance, created with your own class before django.contrib.admin
‘s autodiscover
function is called. I do this by:
-
Having an app that will perform the switch. (I use my project-specific app named
core
for my own purposes.) -
Two choices:
-
Django 1.6 to 1.9: use
__init__
of the app to perform the switch. In Django 1.8, you will get a deprecation warning due to the change in Django 1.9 quoted below. Note that this method will work with 1.9 too because the Django modules loaded by the code shown below have been changed in 1.9 so that they no longer load models. When I use this method mycore/__init__.py
file contains:from django.contrib import admin from django.contrib.admin import sites class MyAdminSite(admin.AdminSite): pass mysite = MyAdminSite() admin.site = mysite sites.site = mysite
-
Django 1.9 and over: use the app configuration of the app to perform the switch. As of Django 1.9, as the release notes state:
All models need to be defined inside an installed application or declare an explicit app_label. Furthermore, it isn’t possible to import them before their application is loaded. In particular, it isn’t possible to import models inside the root package of an application.
I prefer to limit the imports I do at the root level to avoid the risk of loading models. While as of version 1.9 using the
__init__
method above will work, there’s no telling if 1.10 or a later version will introduce a change that will cause problems.When I use this method the
core/__init__.py
setsdefault_app_config = "core.apps.DefaultAppConfig"
and I have acore/apps.py
like this:from django.apps import AppConfig class DefaultAppConfig(AppConfig): name="core" def ready(self): from django.contrib import admin from django.contrib.admin import sites class MyAdminSite(admin.AdminSite): pass mysite = MyAdminSite() admin.site = mysite sites.site = mysite
While it is possible to use this method with versions 1.7 and 1.8, it is a bit risky to use it with those versions. See the notes below.
-
-
Placing this app earlier than
django.contrib.admin
in theINSTALLED_APPS
list. (This is absolutely necessary for 1.7 and later. In earlier versions of Django, it might work okay even if the app is later thandjango.contrib.admin
. However, see the notes below.)
Notes and Caveats
-
The app that performs the switch should really be the first app in the
INSTALLED_APPS
list so as to minimize the chance that something else will grab the value ofsite
fromdjango.contrib.admin
before the switch is made. If another app manages to get that value before the switch is done, then that other app will have a reference to the old site. Hilarity will surely ensue. -
The method above won’t work nicely if two apps are trying to install their own new default admin site class. This would have to be handled on a case-by-case basis.
-
It is possible that a future release of Django could break this method.
-
For version prior to 1.9, I preferred using
__init__
to do site switch over using the app configuration because the documentation on initialization indicates that theready()
method of the app configurations is called relatively late. Between the time an app’s module is loaded, and the timeready()
is called, models have been loaded, and in some case, it could mean that a module has grabbed the value ofsite
fromdjango.contrib.admin
beforeready
is called. So as to minimize the risk, I have the app’s__init__
code do the switch.I believe the risk that existed in version 1.7 and 1.8 and that I avoided by using
__init__
to perform the site switch as early as possible does not exist in 1.9. Everybody is prohibited from loading modules before all the applications are loaded. So doing the switch in theready
callback of the first application listed inINSTALLED_APPS
should be safe. I’ve upgraded a large project to 1.9 and used the app configuration method, without any problem.