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()