Dec 1, 2014

Customize django AdminSite and autodiscover your apps

Sometimes you need to create your own django AdminSite based class. But if you will do it you will loose autodiscover() function and some applications like admin_tools will not work with your application.

I will describe here how to enable auto discovering modules and patch side modules to make it work with new AdminSite based class instance.

Replacing django.contrib.admin package

First of ally you'll need to create your own  admin_custom.py module which will replace original django.contrib.admin package.
# Almost same import as in django.contrib.admin
from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME
from django.contrib.admin.options import ModelAdmin, HORIZONTAL, VERTICAL
from django.contrib.admin.options import StackedInline, TabularInline
# NOTICE: that we are not importing site here!
# basically this is the only one import you'll need
# other imports required if you want easy replace standard admin package with yours
from django.contrib.admin.sites import AdminSite
from django.contrib.admin.filters import (ListFilter, SimpleListFilter,
    FieldListFilter, BooleanFieldListFilter, RelatedFieldListFilter,
    ChoicesFieldListFilter, DateFieldListFilter, AllValuesFieldListFilter)

# Let's create AdminSite instance
# NOTICE: here you can ovverride admin class and create your own AdminSite implementation
site = AdminSite()
Beware from now you can not use standard admin.autodiscover() in your main urls.py and should register your models in your admin_custom.py package manually. Also you must not use now "from django.contrib import admin" in any admin.py.

Using your admin in urls.py

It is pretty simple just do someting like this:
# Uncomment the next two lines to enable the admin:
#from django.contrib import admin #comment original admin
from path.to.your.customadmin import admin_custom as admin
# admin.autodiscover() comment autodiscover
# Unmodified code goes below

Adding custom autodiscover

Probably you will not like that autodiscover() function is not working so do I. Now we'll try to create our custom auto discover in admin_custom.py. Add this to the end of your admin_custom.py:
def autodiscover():
    """
    Autodiscover function from django.contrib.admin
    """

    import copy
    from django.conf import settings
    from django.utils.importlib import import_module
    from django.utils.module_loading import module_has_submodule

    for app in settings.INSTALLED_APPS:
        mod = import_module(app)

        try:
            before_import_registry = copy.copy(site._registry)
            import_module('%s.admin' % app)
        except:
            site._registry = before_import_registry
            if module_has_submodule(mod, 'admin'):
                raise
Now you are need to do some changes in your code:
# IN main urls.py
# Uncomment autodiscover
#from django.contrib import admin #comment original admin
from path.to.your.customadmin import admin_custom as admin
admin.autodiscover()

# IN EVERY admin.py
# Replace django admin import with modified like this:

#from django.contrib import admin #comment original admin
from path.to.your.customadmin import admin_custom as admin

# IF you do not want to change admin.py than you should 
# monkey patch django.contrib.admin module
# in admin_custom.py
# Right after
site = AdminSite()
# add this code
import django.contrib.admin
django.contrib.admin.site = site

# By the way now you can use the standard django admin autodiscover function
# you can import it here
from django.contrib.admin import autodiscover

Django admin_tools patching

Everything described below will work in standard django admin, but if you will use side modules that interacting with django.contrib.admin, than you will have problems. The origin of this problem is that this modules trying to use site variable from original django.contrib.admin, not your's. To make it work you may require to monkey patch this modules. Something like I'm make for the django admin_tools module.
# IN your admin_custom.py
# Right after
site = AdminSite()

# Add
# Monkey patch django admin_tools
import admin_tools.utils
admin_tools.utils.admin.site = site

# Other code goes below