Django Admin Theme Switching Between Grappelli and Jazzmin Not Working (Session-Based)

Problem Statement:

I am working on a Django e-commerce project and want users to switch between two admin themes: Grappelli and Jazzmin using Django sessions.

I have implemented a session-based theme switcher, but:

  • The admin panel always loads Grappelli by default.
  • The switch button appears, but clicking it does not apply the correct theme.
  • After switching to Jazzmin, I get a "Page Not Found" error (404).

Project Setup

1. Installed Apps (settings.py)

INSTALLED_APPS = [
    "grappelli",  #  Defaulting to Grappelli
    "jazzmin",  #  Want to switch between these two
    
    "colorfield",
    "core.apps.CoreConfig",  #  My core app
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "django.contrib.humanize",
]

Grappelli is loading by default, even when the session should switch to Jazzmin.


2. Middleware for Dynamic Theme Switching (core/middleware.py)

from django.utils.deprecation import MiddlewareMixin

class DynamicAdminThemeMiddleware(MiddlewareMixin):
    def process_request(self, request):
        from django.conf import settings

        if request.session.get("admin_theme") == "grappelli":
            settings.INSTALLED_APPS = ["grappelli"] + [app for app in settings.INSTALLED_APPS if app != "jazzmin"]
        else:
            settings.INSTALLED_APPS = ["jazzmin"] + [app for app in settings.INSTALLED_APPS if app != "grappelli"]

I suspect modifying settings.INSTALLED_APPS dynamically might not be effective.
Does Django require a restart for INSTALLED_APPS changes to take effect?

Middleware is included in settings.py:

MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "core.middleware.DynamicAdminThemeMiddleware",  #  Custom middleware
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
]

3. URL Configuration (core/urls.py)

from django.urls import path
from core.views import switch_theme

urlpatterns = [
    path("switch_theme/", switch_theme, name="switch_theme"),  #  Theme switcher
]

Did I miss something in the URL configuration?


4. Theme Switching View (core/views.py)

from django.shortcuts import redirect

def switch_theme(request):
    current_theme = request.session.get("admin_theme", "jazzmin")

    if current_theme == "grappelli":
        # Switch to Jazzmin – redirect to /admin/
        request.session["admin_theme"] = "jazzmin"
        request.session.modified = True  #  Ensure session updates
        return redirect("/admin/")  #  Redirect to Django admin

    else:
        # Switch to Grappelli – redirect to /grappelli/
        request.session["admin_theme"] = "grappelli"
        request.session.modified = True  #  Ensure session updates
        return redirect("/grappelli/")

The session updates, but the theme doesn't switch correctly.
Is the middleware overriding session-based theme switching?


Main Project urls.py

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('grappelli/', include('grappelli.urls')),
    path('admin/', admin.site.urls),
    path("", include("core.urls")),
]

5. Admin Template Modification (templates/admin/base_site.html)

{% extends "admin/base.html" %}

{% block branding %}
  <h1>My Custom Admin Panel</h1>
{% endblock %}

{% block userlinks %}
  {{ block.super }}

  <p>DEBUG: Admin Theme is: {{ request.session.admin_theme }}</p>

  {% if request.session.admin_theme == "grappelli" %}
      <a href="{% url 'core:switch_theme' %}">Switch to Jazzmin</a>
  {% else %}
      <a href="{% url 'core:switch_theme' %}">Switch to Grappelli</a>
  {% endif %}
{% endblock %}

The switcher appears, but the session is not applied correctly.
Is there an issue with how I'm referencing request.session.admin_theme?


Issue I Am Facing

  1. Theme Always Loads as Grappelli
    • Even when the session should switch to Jazzmin.
  2. Clicking "Switch Theme" Redirects to a 404 Page
    • /admin/ is not loading Jazzmin properly.
  3. Switching Doesn't Persist
    • Even though request.session["admin_theme"] changes, the applied theme does not.

What I Have Tried

  • Restarted Django server after modifying settings:
    python manage.py runserver
    
  • Cleared browser cache and cookies to ensure sessions refresh.
  • Checked session updates by printing request.session.admin_theme.
  • Tried different redirect URLs (reverse("admin:index") instead of /admin/).
  • Verified INSTALLED_APPS order and middleware execution.

Questions

  1. Can INSTALLED_APPS be modified dynamically in middleware, or does Django require a restart?
  2. Why is Jazzmin giving a 404 error when switching?
  3. Is there a better way to apply admin themes dynamically using sessions?

System Information

  • Django Version: 5.1.3
  • Python Version: 3.12.5
  • Jazzmin Version: 3.0.1
  • Grappelli Version: 4.0.1
  • Database: SQLite

Expected Behavior

  • /admin/ should load Jazzmin.
  • /grappelli/ should load Grappelli.
  • Clicking Switch Theme should apply the correct theme without errors.

**Any help would be greatly appreciated! Thanks in advance! **

Since INSTALLED_APPS cannot be changed at runtime, the best way to implement theme switching is to:

  1. Define two separate admin sites (one for Jazzmin, one for Grappelli).

  2. Use a session-based switcher to dynamically assign the active admin site.

  3. Ensure proper URL routing so that Django knows which admin to load.

Create a Custom Admin Site for Jazzmin and Grappelli

Modify core/admin.py:

from django.contrib.admin import AdminSite
from django.conf import settings

class GrappelliAdminSite(AdminSite):
    site_header = "Grappelli Admin"
    site_title = "Grappelli Admin"
    index_title = "Welcome to Grappelli Admin"

class JazzminAdminSite(AdminSite):
    site_header = "Jazzmin Admin"
    site_title = "Jazzmin Admin"
    index_title = "Welcome to Jazzmin Admin"

# Instances of admin sites
grappelli_admin_site = GrappelliAdminSite(name="grappelli_admin")
jazzmin_admin_site = JazzminAdminSite(name="jazzmin_admin")

Register Models with Both Admin Sites

In core/admin.py, register models with both sites:

from django.contrib import admin
from .models import Product, Category
from .admin import grappelli_admin_site, jazzmin_admin_site

# Register your models with both admin sites
for model in [Product, Category]:
    admin.site.register(model)  # Default admin site
    grappelli_admin_site.register(model)
    jazzmin_admin_site.register(model)

Define a URL-Based Switcher for Admin Panel

Modify core/urls.py

from django.urls import path, include
from core.views import switch_theme
from core.admin import grappelli_admin_site, jazzmin_admin_site
from django.contrib import admin

urlpatterns = [
    path("switch_theme/", switch_theme, name="switch_theme"),  # Theme switcher
    
    # Route based on session theme
    path("grappelli-admin/", grappelli_admin_site.urls),
    path("jazzmin-admin/", jazzmin_admin_site.urls),
    
    # Default Django admin (fallback)
    path("admin/", admin.site.urls),
]

Implement the Theme Switching View

Modify core/views.py:


from django.shortcuts import redirect

def switch_theme(request):
    # Toggle between themes
    current_theme = request.session.get("admin_theme", "jazzmin")

    if current_theme == "grappelli":
        request.session["admin_theme"] = "jazzmin"
        return redirect("/jazzmin-admin/")  # Redirect to Jazzmin admin
    else:
        request.session["admin_theme"] = "grappelli"
        return redirect("/grappelli-admin/")  # Redirect to Grappelli admin

Modify Admin Template for Theme Switching

Modify templates/admin/base_site.html:

{% extends "admin/base.html" %}

{% block branding %}
  <h1>My Custom Admin Panel</h1>
{% endblock %}

{% block userlinks %}
  {{ block.super }}

  <p>Current Admin Theme: {{ request.session.admin_theme }}</p>

  {% if request.session.admin_theme == "grappelli" %}
      <a href="{% url 'switch_theme' %}">Switch to Jazzmin</a>
  {% else %}
      <a href="{% url 'switch_theme' %}">Switch to Grappelli</a>
  {% endif %}
{% endblock %}

Expected Behavior

  • /grappelli-admin/ → Loads Grappelli Admin.

  • /jazzmin-admin/ → Loads Jazzmin Admin.

  • Clicking "Switch Theme" correctly switches between Jazzmin and Grappelli.

I hope it will help you.

Вернуться на верх