The other day I was asked to add a new page on the admin site of django that was like a list view for a model but with different actions, different columns to display and with a filter applied to the list. After hours of searching on the docs I ended up reading the source code of the contrib.admin package to understand how to do it.
Feel free to skip all this text and inspect the code directly on github
Let’s use the user model to create a new view on the admin. In this new view we want to display only the list of inactive users that are not admins of the site. And in this view we want to display the emails, date joined and last login. BUT we also want to keep our complete users page.
We’ll need une view /users/ to display the complete list and /users/inactive/ to display only the inactive users
This is where we customize the admin pages. It’s a single file
# -*- coding: utf-8 -*- ''' Created on 2015-03-24 19:17 @summary: custom admin page for users @author: pablo ''' from django.contrib import admin from django.contrib.auth.models import User from django.conf.urls import patterns, url from functools import update_wrapper from .admin_views import InactiveUsersView class UserAdmin(admin.ModelAdmin): list_display = ('username', 'email', 'is_active', 'is_staff', 'is_superuser') def get_urls(self): # this is just a copy paste from the admin code def wrap(view): def wrapper(*args, **kwargs): return self.admin_site.admin_view(view)(*args, **kwargs) return update_wrapper(wrapper, view) # get the default urls urls = super(UserAdmin, self).get_urls() # define my own urls my_urls = patterns( '', url(r'^inactive/$', wrap(self.changelist_view), name="inactive_users") ) # return the complete list of urls return my_urls + urls def get_changelist(self, request): """ This method must return the view to be used for listing the model """ # for inactive users use the InactiveUsersView if request.resolver_match.url_name == "inactive_users": return InactiveUsersView return super(UserAdmin, self).get_changelist(request) # we must unregister the default user model admin.site.unregister(User) admin.site.register(User, UserAdmin)
The custom views
I wanted to separate the code and have the admin views in a different file but you could put it in the admin.py file too
from django.contrib.admin.views.main import ChangeList class InactiveUsersView(ChangeList): """ This view displays the list of inactive users """ def __init__(self, *args, **kwargs): super(InactiveUsersView, self).__init__(*args, **kwargs) self.list_display = ('username', 'email', 'date_joined', 'last_login') def get_queryset(self, request): qs = super(InactiveUsersView, self).get_queryset(request) # filter inactive and admin users return qs.filter(is_staff=False, is_active=False, is_superuser=False)
The default users list view
The custom users list view
I tried and tried and searched and read the docs, and I wasn’t able to find a way to add a custom link on the index page of the admin (without installing another package that manages the admin). If you are using django-suit you could add a custom link on the sidebar
When you are customizing your admin site, you end up writing a lot of code inside the admin.py file and it might get dirty. It’s better to separate your code in multiple files. I also tried to create an admin submodule that works fine on production, but not in the unit tests (because of the admin registry)
Hope this helps