Search code examples
djangodjango-adminpython-3.4django-authenticationdjango-1.8

Auto register Django auth models using custom admin site


I implemented authentication management using Django auth with the default admin site but then I wanted to use my own AdminSite to rewrite some behaviors:

class OptiAdmin(admin.AdminSite):
    site_title = "Optimizer site's admin"
    #...Other stuff here

Then registered my own models:

admin_site = OptiAdmin(name='opti_admin')
admin.site.register(MyModel, MyModelAdmin)
#Other stuff here

But when I go to the admin site I am only able to see the models I just registered, which sounds fair to me but I would like to see all the other apps models in this new custom site including the auth's users and groups and I don't know how to do this automatically like the default admin does, pls help :).


Solution

    1. Create your own AdminSite with a simple __init__() override.
    2. Import your admin in urls.py.

    Replacing the Django Admin and getting the autodiscover() behavior is possible with minimal effort. Here's a project structure generated in the typical django-admin startproject project fashion:

    project/
        manage.py
        project/
            __init__.py
            settings.py
            urls.py
            wsgi.py
            admin.py  # CREATE THIS FILE
    

    project/admin.py: (I think it makes the most sense to do this at the project level.)

    from django.contrib.admin import *  # PART 1
    
    class MyAdminSite(AdminSite):
        site_header = "My Site"
    
        def __init__(self, *args, **kwargs):
            super(MyAdminSite, self).__init__(*args, **kwargs)
            self._registry.update(site._registry)  # PART 2
    
    site = MyAdminSite()
    

    project/urls.py (snippet):

    from . import admin  # PART 3
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
    ]
    

    Part 1 is simple Python. By importing everything from django.contrib.admin into your namespace, it acts as a drop-in replacement. I suppose you don't have to do this, but it helps preserve expectations. Part 3, simply connect up your admin. Part 2 is the real trick. As the documentation says, autodiscover() is called to do the work. All autodiscover does is go through INSTALLED_APPS attempting to import a file called admin.py. Importing runs the code of course and that code is doing the same thing you do to register models (example by decorator and example by method). No magic. You don't have to register your models with your customized admin (as the documentation says).

    Autodiscover looks smarter than it is with its register_to kwarg. That indicates you could call autodiscover() yourself passing your own admin. Nope; there's no wiring connected there (future feature?). The assignment happens here and is fixed to the native AdminSite instance here (or here using the decorator). Django contrib models register to that instance and so will any third-party libraries. It's not something you can hook into.

    Here's the trick though, _registry is just a dictionary mapping. Let Django autodiscover all the things and then just copy the mapping. That's why self._registry.update(site._registry) works. "self" is your customized AdminSite instance, "site" is Django's instance and you can register your models with either.

    (Final note: If models are missing, it's because of import order. All the registration to Django's AdminSite needs to happen before you copy _registry. Registering directly to your customized admin is probably the easiest thing.)