One of the great builtin features of Django is the admin app. It lets you, among other things, execute the usual CRUD operations on your data, search, filter and execute bulk actions on many records.
However the interface is a bit rigid, by default you have the “dashboard” with the list of models, the page listing your records for each model and the details page for each individual item.
What if you want to display other information to the admins? Such an overview of the system, aggregate data, some statistics, etc.
In this post I will address the steps required to add custom pages to the admin and include them in the main menu of the administration.
The starting point will be the website I used in a previous tip/post:
1. Add a custom Admin Site
The first step is to create a new custom admin website, so we can easily modify its contents and functionality.
from django.contrib import admin
class YourCustomAdminSite(admin.AdminSite):
pass
Now we will have to use this admin site across your project, which means changing your current admin settings to use this “site” (such as your ModelAdmin
s and your urls.py
.
If the above is too much trouble and requires to many changes, this small “hack” before including the admin URLs will also work:
# urls.py
admin.site.__class__ = YourCustomAdminSite
2. Add a new view
In our new admin site we can now create views as methods, to handle different requests. Like this:
from django.contrib import admin
from django.template.response import TemplateResponse
class YourCustomAdminSite(admin.AdminSite):
def custom_page(self, request):
context = {"text": "Hello Admin",
"page_name": "Custom Page"}
return TemplateResponse(request,
"admin/custom_page.html",
context)
3. Add a new template
As you can see in the above python snippet, we are using a template that doesn’t exist yet, so lets create it:
{# templates/admin/custom_page.html #}
{% extends 'admin/change_list.html' %}
{% block pagination %}{% endblock %}
{% block filters %}{% endblock filters %}
{% block object-tools %}{% endblock object-tools %}
{% block search %}{% endblock %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">Home</a>
{% if page_name %} › {{ page_name }}{% endif %}
</div>
{% endblock %}
{% block result_list %}
{{text}}
{% endblock result_list %}
(I chose to extend admin/change_list.html
, but the content of this template is up to you, no restrictions here. If you decide to not extend any existing admin template, the last steps of 5. will not apply to your case)
4. Extend the admin URLs
To make this new endpoint accessible, we now have to edit the method that returns all existing URLs of the administration and append our new page.
from django.urls import path
...
class YourCustomAdminSite(admin.AdminSite):
...
def get_urls(
self,
):
return [
path(
"custom_page/",
self.admin_view(self.custom_page),
name="custom_page",
),
] + super().get_urls()
At this point the custom page should be available at /admin/custom_page/
, but users will not be able to find it, since no other link or button points to it.
5. Extend the menu items
The final step is to include a link to the new page in the existing menu, in order for it to be easily accessible. First we need to replace the current index_template
:
...
class YourCustomAdminSite(admin.AdminSite):
index_template = "admin/custom_index.html"
...
And add the following content to your new template:
{% extends "admin/index.html" %}
{% block content %}
<div id="content-main">
{% include "admin/custom_app_list.html" with app_list=app_list show_changelinks=True %}
</div>
{% endblock %}
The included custom_app_list.html
should look like this:
<div id="extra_links_wrapper" class="module">
<table>
<caption>
<a class="section" title="Custom Pages">Custom Pages</a>
</caption>
<tr>
<th scope="row">
<a href="{% url 'admin:custom_page' %}">
Custom Page
</a>
</th>
<td></td>
</tr>
</table>
</div>
{% include 'admin/app_list.html' %}
Basically we added a new section to the list containing a link to our new page.
Looking good, the page is there with our content and is accessible through the index page. However many traditional elements on the page are missing (side menu, logout button, etc). To add them we just need some small changes to our view:
...
class YourCustomAdminSite(admin.AdminSite):
...
def custom_page(self, request):
context = {
"text": "Hello Admin",
"page_name": "Custom Page",
"app_list": self.get_app_list(request),
**self.each_context(request),
}
return TemplateResponse(request, "admin/custom_page.html", context)
Now it looks a lot better:
But something is still missing. Where are the links to our custom pages?
It seems the standard app_list.html
is being used. Lets replace it with our custom one by overriding nav_sidebar.html
:
{# templates/admin/nav_sidebar.html #}
{% load i18n %}
<button class="sticky toggle-nav-sidebar" id="toggle-nav-sidebar"
aria-label="{% translate 'Toggle navigation' %}"></button>
<nav class="sticky" id="nav-sidebar">
{% include 'admin/custom_app_list.html' with app_list=available_apps show_changelinks=False %}
</nav>
Note: The app where you put the above template must be placed before the “admin” app in your “INSTALLED_APPS” list, otherwise the default template will be used anyway.
And for today, this is it. With the above changes you can add as many custom pages to your admin as you need and have full controls over their functionality.