25

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 :).

gerosalesc
  • 2,983
  • 3
  • 27
  • 46

5 Answers5

24
  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.)

JCotton
  • 11,650
  • 5
  • 53
  • 59
  • I've just tried this with Django 1.11 with a custom AdminSite instance. The registered models take all of the defaults from the parent AdminSite and not the relevant defaults of the child instance. For example the site_title is set to a different value in my custom instance and when viewing any of the models that have been auto registered the Django defaults are visible only for those models. The docs do say that you are expected to manually register when using a custom instance. https://docs.djangoproject.com/en/1.11/ref/contrib/admin/#django.contrib.admin.autodiscover – Aiky30 Oct 02 '17 at 15:14
  • Did you try the `self._registry.update()` step where Django has auto-registered/built its AdminSite and then you copy the mapping? That may overwrite existing values, so try setting your AdminSite attributes (like site_title, header, url, etc) after that call in `__init__()`. – JCotton Oct 02 '17 at 17:14
  • Yes I tried it all as you posted. So if i understand this correctly you want me to redefine site_header etc in the __init__ function after the self._registry_update? – Aiky30 Oct 05 '17 at 10:56
  • Yes, worth a try. I glanced at the 1.11 code and it's the same as 1.9. Try inspecting during the process with print() or pdb break points. You probably have it all correct (except for the one thing that always burns the time). – JCotton Oct 05 '17 at 16:54
  • 1
    Tested this code in `Django 2.0`. Setting up `site_header` or `site_title` works only for `index` and `app index` pages. The `app models` however still shows `Django Administration`. – Da CodeKid Dec 19 '17 at 02:51
12

The Django docs suggest using SimpleAdminConfig with a custom admin site.

INSTALLED_APPS = (
    ...
    'django.contrib.admin.apps.SimpleAdminConfig',
    ...
)

That prevents the models being registered with the default AdminSite.

The docs seem to assume that you will import the models individually and add them to your custom admin site:

from django.contrib.auth.models import Group, User
from django.contrib.auth.admin import GroupAdmin, UserAdmin

admin_site.register(Group, GroupAdmin)
admin_site.register(User, UserAdmin)

This would be very repetitive if you have models in many apps. It doesn't offer any advice how to automatically register models from all your apps with your custom site.

You could try monkey patching admin, and replacing admin.site with your own.

from django.contrib import admin
admin.site = OptiAdmin(name='opti_admin')

Then, when code called admin.site.register(), it would register the model with your admin site. This code would have to run before any models were registered. You could try putting it in the AppConfig for your app, and make sure that your app is above django.contrib.admin.

Alasdair
  • 298,606
  • 55
  • 578
  • 516
  • @Alasdir Thks, I will try this monkey patching later today, sounds like it could work. Also, the manual registering will do the job for sure but I would need to register every models in every app I want included – gerosalesc Sep 16 '15 at 18:16
  • It works for me using `import django.contrib.admin` `django.contrib.admin.sites.site = admin_site` `django.contrib.admin.site = admin_site` – beruic Feb 13 '17 at 14:13
6

Adding to JCotton's great answer:

Using django 2.0, overriding site_header and site_title in the custom admin site only works for the index page.

To get it to work with all admin views, extend JCotton's code with the following:

    def __init__(self, *args, **kwargs):
        super(MyAdminSite, self).__init__(*args, **kwargs)
        self._registry.update(site._registry)  # PART 2

        for model, model_admin in self._registry.items():
            model_admin.admin_site = self
Or Zarchi
  • 61
  • 2
  • 1
1

Just include init method in your CustomAdminSite class like this.

    class CustomAdminSite(admin.AdminSite):
        def __init__(self, *args, **kwargs):
            super(CustomAdminSite, self).__init__(*args, **kwargs)
            self._registry.update(admin.site._registry)
Mansoor Ul Haq
  • 320
  • 3
  • 8
0

For anyone still running into this issue:

If you define a custom AdminSite and configure it within a custom AdminConfig, you shouldn't need to manually register third party models/model forms for your own site (see Django implementation for how it uses this to find the appropriate AdminSite)

In your example, now that you have your OptiAdmin, you need to define something like the following:

# my_project.apps.py
from django.contrib.admin.apps import AdminConfig

class OptiAdminConfig(AdminConfig)
  default_site = 'my_project.admin.OptiAdmin'

Then, in your settings.py file, you need to set it within your INSTALLED_APPS configuration like the following:

INSTALLED_APPS = [
    ...
    "my_project.apps.OptiAdminConfig",
    ...

]

Now you should see any models registered by third party apps in the Django admin site.

Andrew R
  • 11
  • 1