Customize the Django Admin
Last updated
Last updated
The Django framework comes with a powerful administrative tool called admin. You can use it out of the box to quickly add, delete, or edit any database model from a web interface. But with a little extra code, you can customize the Django admin to take your admin capabilities to the next level.
In this tutorial, you’ll learn how to:
Add attribute columns in the model object list
Link between model objects
Add filters to the model object list
Make model object lists searchable
Modify the object edit forms
Override Django admin templates
Free Bonus: Click here to get access to a free Django Learning Resources Guide (PDF) that shows you tips and tricks as well as common pitfalls to avoid when building Python + Django web applications. Remove ads
To get the most out of this tutorial, you’ll need some familiarity with Django, particularly model objects. As Django isn’t part of the standard Python library, it’s best if you also have some knowledge of pip
and pyenv
(or an equivalent virtual environment tool). To learn more about these topics, check out the following resources:
You may also be interested in one of the many available Django tutorials.
The code snippets in this tutorial were tested against Django 3.0.7. All the concepts predate Django 2.0, so they should work in whatever version you’re using, but minor differences may exist.
The Django admin provides a web-based interface for creating and managing database model objects. To see it in action, you’ll first need a Django project and some object models. Install Django inside a clean virtual environment:
You first create a new Django project named School
with an app called core
. Then you migrate the authentication tables and create an administrator. Access to the Django admin screens is restricted to users with staff
or superuser
flags, so you use the createsuperuser
management command to create a superuser
.
You also need to modify School/settings.py
to include the new app named core
:
The core
app directory will start with the following files inside:
You’re interested in two of these files:
models.py
defines your database models.
admin.py
registers your models with the Django admin.
To demonstrate the outcome when you customize the Django admin, you’ll need some models. Edit core/models.py
:
These models represent students taking courses at a school. A Course
has a name
and a year
in which it was offered. A Person
has a first and last name and can take zero or more courses
. A Grade
contains a percentage score that a Person
received on a Course
.
Here’s a model diagram showing the relationships between the objects:
The underlying table names in the database are slightly different from this, but they’re related to the models shown above.
Each model that you want Django to represent in the admin interface needs to be registered. You do this in the admin.py
file. Models from core/models.py
are registered in the corresponding core/admin.py
file:
You’re almost ready to go. Once you’ve migrated your database models, you can run the Django development server and see the results:
Now visit http://127.0.0.1:8000/admin
to see your admin interface. You’ll be prompted to log in. Use the credentials you created with the createsuperuser
management command.
The admin home screen lists all the registered database models:
You can now use the interface to create objects in your database. Clicking a model name will show you a screen listing all the objects in the database for that model. Here’s the Person
list:
The list starts out empty, like your database. Clicking ADD PERSON allows you to create a person in the database. Once you save, you’ll be returned to the list of Person
objects:
The good news is you’ve got an object. The bad news is Person object (1)
tells you the id
of the object and nothing else. By default, the Django admin displays each object by calling str()
on it. You can make this screen a little more helpful by adding a .__str__()
method to the Person
class in core/models.py
:
Adding Person.__str__()
changes the display to include the first and last name of the Person
in the interface. You can refresh the screen to see the change:
That’s a little better! Now you can see some information about the Person
object. It’s a good idea to add similar methods to both the Course
and the Grade
objects:
You’ll want to have some data in your database to see the full effect of your customizations. You can have some fun and create your own data now, or you can skip the work and use a fixture. Expand the box below to learn how to load data using a fixture.
Loading Fixtures in DjangoShow/Hide
Now that you have some data to work with, you’re ready to start customizing Django’s admin interface. Remove ads
The smart folks who created the Django framework not only built the admin, but they did it in such a way that you can customize it for your projects. When you registered the PersonAdmin
object earlier, it inherited from admin.ModelAdmin
. Most of the customization you can do with the Django admin is done by modifying ModelAdmin
, and you sure can modify it!
ModelAdmin
has over thirty attributes and almost fifty methods. You can use each one of these to fine-tune the admin’s presentation and control your objects’ interfaces. Every one of these options is described in detail in the documentation.
To top it all off, the admin is built using Django’s templating interface. The Django template mechanism allows you to override existing templates, and because the admin is just another set of templates, this means you can completely change its HTML.
Although it’s beyond the scope of this tutorial, you can even create multiple admin sites. That might seem like overkill, but it allows you to get fancy and define different sites for users with different permissions.
The Django admin is split into three major areas:
App index
Change lists
Change forms
The app index lists your registered models. A change list is automatically created for each registered model and lists the objects for that model. When you add or edit one of those objects, you do so with a change form.
In the earlier example, the app index showed the Person
, Course
, and Grade
objects. Clicking People shows the change lists for Person
objects. On the Person
change list page, clicking the Buffy Summers
object takes you to the change form to edit Buffy’s details.
list_display
Implementing .__str__()
is a quick way to change the representation of a Person
object from a meaningless string to understandable data. Since this representation will also show up in drop-downs and multi-selects, you definitely want to make it as easy to understand as possible.
You can customize change list pages in far more ways than just modifying an object’s string representation. The list_display
attribute of an admin.ModelAdmin
object specifies what columns are shown in the change list. This value is a tuple of attributes of the object being modeled. For example, in core/admin.py
, modify PersonAdmin
as follows:
The code above modifies your Person
change list to display the last_name
and first_name
attributes for each Person
object. Each attribute is shown in a column on the page:
The two columns are clickable, allowing you to sort the page by the column data. The admin also respects the ordering
attribute of a Meta
section:
Adding the ordering
attribute will default all queries on Person
to be ordered by last_name
then first_name
. Django will respect this default order both in the admin and when fetching objects.
The list_display
tuple can reference any attribute of the object being listed. It can also reference a method in the admin.ModelAdmin
itself. Modify PersonAdmin
again:
In the above code, you add a column to the admin that displays each student’s grade average. show_average()
is called once for each object displayed in the list.
The obj
parameter is the object for the row being displayed. In this case, you use it to query the corresponding Grade
objects for the student, with the response averaged over Grade.grade
. You can see the results here:
Keep in mind that the average grade should really be calculated in the Person
model object. You’ll likely want the data elsewhere, not just in the Django admin. If you had such a method, you could add it to the list_display
attribute. The example here shows what you can do in a ModelAdmin
object, but it probably isn’t the best choice for your code.
By default, only those columns that are object attributes are sortable. show_average()
is not. This is because sorting is performed by an underlying QuerySet
, not on the displayed results. There are ways of sorting these columns in some cases, but that’s beyond the scope of this tutorial.
The title for the column is based on the name of the method. You can alter the title by adding an attribute to the method:
By default, Django protects you from HTML in strings in case the string is from user input. To have the display include HTML, you must use format_html()
:
show_average()
now has a custom title, "Average"
, and is formatted to be in italics:
Unfortunately, Django hasn’t yet added f-string support for format_html()
, so you’re stuck with str.format()
syntax. Remove ads
It’s quite common for objects to reference other objects through the use of foreign keys. You can point list_display
at a method that returns an HTML link. Inside core/admin.py
, modify the CourseAdmin
class as follows:
This code causes the Course
change list to have three columns:
The course name
The year in which the course was offered
A link displaying the number of students in the course
You can see the resulting change in the following screenshot:
When you click 2 Students, it takes you to the Person
change list page with a filter applied. The filtered page shows only those students in Psych 101
, Buffy and Willow. Xander didn’t make it to university.
The example code uses reverse()
to look up a URL in the Django admin. You can look up any admin page using the following naming convention:
This name structure breaks down as follows:
admin:
is the namespace.
app
is the name of the app.
model
is the model object.
page
is the Django admin page type.
For the view_students_link()
example above, you use admin:core_person_changelist
to get a reference to the change list page of the Person
object in the core
app.
Here are the available URL names:
Page
URL Name
Purpose
Change list
%(app)s\_%(model)s\_changelist
Model object page list
Add
%(app)s\_%(model)s\_add
Object creation page
History
%(app)s\_%(model)s\_history
Object change history page
Takes an object_id
as a parameter
Delete
%(app)s\_%(model)s\_delete
Object delete page
Takes an object_id
as a parameter
Change
%(app)s\_%(model)s\_change
Object edit page
Takes an object_id
as a parameter
You can filter the change list page by adding a query string to the URL. This query string modifies the QuerySet
used to populate the page. In the example above, the query string "?courses__id={obj.id}"
filters the Person
list to only those objects that have a matching value in Person.course
.
These filters support QuerySet
field lookups using double underscores (__
). You can access attributes of related objects as well as use filter modifiers like __exact
and __startswith
.
You can find the full details on what you can accomplish with the list_display
attribute in the Django admin documentation.
In addition to filtering data on the change list through the calling URL, you can also filter with a built-in widget. Add the list_filter
attribute to the CourseAdmin
object in core/admin.py
:
The list_filter
will display a new section on the page with a list of links. In this case, the links filter the page by year. The filter list is automatically populated with the year
values used by the Course
objects in the database:
Clicking a year on the right-hand side will change the list to include only Course
objects with that year
value. You can also filter based on the attributes of related objects using the __
field lookup syntax. For example, you could filter GradeAdmin
objects by course__year
, showing the Grade
objects for only a certain year of courses.
If you’re looking for more control over your filtering, then you can even create filter objects that specify the lookup attributes and the corresponding QuerySet
. Remove ads
Filters aren’t the only way to reduce the amount of data on the screen. Django admin also supports searching through the search_fields
option, which adds a search box to the screen. You set it with a tuple containing the names of fields to be used for constructing a search query in the database.
Anything the user types in the search box is used in an OR
clause of the fields filtering the QuerySet
. By default, each search parameter is surrounded by %
signs, meaning if you search for r
, then any word with an r
inside will appear in the results. You can be more precise by specifying a __
modifier on the search field.
Edit the PersonAdmin
in core/admin.py
as follows:
In the above code, searching is based on last name. The __startswith
modifier restricts the search to last names that begin with the search parameter. Searching on R
provides the following results:
Whenever a search is performed on a change list page, the Django admin calls your admin.ModelAdmin
object’s get_search_results()
method. It returns a QuerySet
with the search results. You can fine-tune searches by overloading the method and changing the QuerySet
. More details can be found in the documentation.
You can customize more than just the change list page. The screens used to add or change an object are based on a ModelForm
. Django automatically generates the form based on the model being edited.
You can control which fields are included, as well as their order, by editing the fields
option. Modify your PersonAdmin
object, adding a fields
attribute:
The Add and Change pages for Person
now put the first_name
attribute before the last_name
attribute even though the model itself specifies the other way around:
ModelAdmin.get_form()
is responsible for creating the ModelForm
for your object. You can override this method to change the form. Add the following method to PersonAdmin
:
Now, when the Add or Change page is displayed, the label of the first_name
field will be customized.
Changing the label might not be sufficient to prevent vampires from registering as students. If you don’t like the ModelForm
that the Django admin created for you, then you can use the form
attribute to register a custom form. Make the following additions and changes to core/admin.py
:
The above code enforces additional validation on the Person
Add and Change pages. ModelForm
objects have a rich validation mechanism. In this case, the first_name
field is being checked against the name "Spike"
. A ValidationError
prevents students with this name from registering:
By changing or replacing the ModelForm
object, you can fully control the appearance and validation of the pages you use to add or change object pages. Remove ads
The Django developers implemented the admin using the Django template mechanism. This made their job a little bit easier, but it also benefits you by allowing you to override the templates. You can fully customize the admin by changing the templates used to render pages.
You can see all the templates used in the admin by looking inside the Django package in your virtual environment:
The Django template engine has a defined order for loading templates. When it loads a template, it uses the first template that matches the name. You can override admin templates by using the same directory structure and file names.
The admin templates come in two directories:
admin
is for the model object pages.
registration
is for password changes and logging in and out.
To customize the logout page, you need to override the right file. The relative path leading to the file has to be the same as the one being overridden. The file you’re interested in is registration/logged_out.html
. Start by creating the directory in the School
project:
Now tell Django about your new template directory inside your School/settings.py
file. Look for the TEMPLATES
directive and add the folder to the DIR
list:
The template engine searches directories in the DIR
option before the application directories, so anything with the same name as an admin template will be loaded instead. To see this in action, copy the logged_out.html
file into your templates/registration
directory, then modify it:
You’ve now customized the logout page. If you click LOG OUT, then you’ll see the customized message:
Django admin templates are deeply nested and not very intuitive, but you have full control over their presentation if you need it. Some packages, including Grappelli and Django Admin Bootstrap, have fully replaced the Django admin templates to change their appearance.
Django Admin Bootstrap is not yet compatible with Django 3, and Grappelli only recently added support, so it may still have some issues. That being said, if you want to see the power of overriding admin templates, then check out those projects!
The Django admin is a powerful built-in tool giving you the ability to create, update, and delete objects in your database using a web interface. You can customize the Django admin to do almost anything you want.
In this tutorial, you learned how to:
Register your object models with the Django admin
Add attributes as columns in the change list
Create column values with calculated content
Cross-reference admin pages through links
Filter the change list page through query strings
Make your change list searchable
Customize the automatic ModelForm
object
Change the HTML in Django admin templates
This tutorial only touches the surface. The amount of configuration you can do to customize the Django admin is staggering. You can take a deeper dive into the documentation to explore such topics as inline forms, multiple admin
sites, mass editing, auto-completion, and much more. Happy coding!
Reference : https://realpython.com/customize-django-admin-python/#prerequisites