Skip to main content

Managing Multiple Admin Sites

In this codebase, the admin interface is not a singleton; it is an instance of the AdminSite class. While most projects use the default instance provided at django.contrib.admin.site, you can create multiple, independent admin interfaces within a single project to serve different user groups or administrative purposes.

The Role of AdminSite

The AdminSite class, defined in django/contrib/admin/sites.py, encapsulates an entire instance of the Django admin application. It is responsible for:

  • Model Registry: Maintaining a mapping of model classes to their corresponding ModelAdmin instances in self._registry.
  • Branding: Defining the visual identity of the admin via attributes like site_header, site_title, and index_title.
  • URL Routing: Generating the set of URLs required to navigate the admin via get_urls().
  • Authentication: Managing permissions and login/logout views through methods like has_permission(), login(), and logout().

Creating Custom Admin Sites

To create a specialized admin interface, you typically subclass AdminSite to override its default behavior or branding.

from django.contrib.admin import AdminSite

class CustomerServiceAdminSite(AdminSite):
site_header = "Customer Support Management"
site_title = "Support Portal"
index_title = "Support Dashboard"

# Instantiate the custom site with a unique name
support_admin_site = CustomerServiceAdminSite(name="support_admin")

The Importance of the Name Argument

When instantiating AdminSite, the name argument (defaulting to "admin") is critical. It serves as the URL namespace for the site. If you host multiple admin sites, each must have a unique name to prevent collisions when reversing URLs.

As seen in AdminSite.__init__:

def __init__(self, name="admin"):
self._registry = {} # model_class class -> admin_class instance
self.name = name
# ...
all_sites.add(self)

Independent Model Registries

Each AdminSite instance maintains its own _registry. This allows you to register the same model with different ModelAdmin configurations across different sites. For example, a "Superuser Admin" might show all fields of a User model, while a "Staff Admin" might only show a subset.

from .models import Article
from .admin import ArticleAdmin, LimitedArticleAdmin

# Register with the default site
admin.site.register(Article, ArticleAdmin)

# Register with a custom site using a different ModelAdmin
support_admin_site.register(Article, LimitedArticleAdmin)

The register method in django/contrib/admin/sites.py ensures that models are only registered within that specific instance's _registry.

Hooking Sites into URLconf

To make an admin site accessible, you must include its urls property in your project's URL configuration. The urls property returns a tuple containing the URL patterns, the application name (always "admin"), and the instance name.

# urls.py
from django.urls import path
from myproject.admin import support_admin_site

urlpatterns = [
path('admin/', admin.site.urls),
path('support-admin/', support_admin_site.urls),
]

URL Reversing and current_app

Because multiple admin sites share the same application name ("admin"), you must specify the current_app when reversing URLs to ensure Django resolves the path for the correct instance.

Inside AdminSite methods, this is handled by passing self.name. For example, in AdminSite.password_change:

url = reverse("admin:password_change_done", current_app=self.name)

When reversing URLs in your own views or templates for a specific site, you should follow this pattern:

from django.urls import reverse

# Link to the index of the custom support admin
url = reverse('admin:index', current_app='support_admin')

Overriding the Default Admin Site

The project uses DefaultAdminSite (a LazyObject) to provide the standard admin.site instance. This object determines which class to instantiate by looking at the default_site attribute of the admin application's configuration.

In django/contrib/admin/apps.py, the SimpleAdminConfig defines the default:

class SimpleAdminConfig(AppConfig):
default_site = "django.contrib.admin.sites.AdminSite"

To replace the default admin.site globally with your custom subclass, you can create a custom AppConfig:

# myproject/apps.py
from django.contrib.admin.apps import AdminConfig

class MyCustomAdminConfig(AdminConfig):
default_site = "myproject.admin.MyAdminSite"

Then, replace 'django.contrib.admin' in your INSTALLED_APPS with 'myproject.apps.MyCustomAdminConfig'. The DefaultAdminSite._setup() method will then import and instantiate your custom class instead of the standard AdminSite.

class DefaultAdminSite(LazyObject):
def _setup(self):
AdminSiteClass = import_string(apps.get_app_config("admin").default_site)
self._wrapped = AdminSiteClass()