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
- Theme Always Loads as Grappelli
- Even when the session should switch to Jazzmin.
- Clicking "Switch Theme" Redirects to a 404 Page
/admin/
is not loading Jazzmin properly.
- Switching Doesn't Persist
- Even though
request.session["admin_theme"]
changes, the applied theme does not.
- Even though
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
- Can
INSTALLED_APPS
be modified dynamically in middleware, or does Django require a restart? - Why is Jazzmin giving a 404 error when switching?
- 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:
Define two separate admin sites (one for Jazzmin, one for Grappelli).
Use a session-based switcher to dynamically assign the active admin site.
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.