A web-based administrative back end is a standard feature of modern websites. The administrative interface, or admin for short, allows trusted site administrators to create, edit and publish content, manage site users, and perform other administrative tasks.
Django comes with a built-in admin interface. With Django’s admin you can authenticate users, display and handle forms, and validate input; all automatically. Django also provides a convenient interface to manage model data.
In this chapter, we will explore the basics of the Django admin—create a superuser login, register models with the admin, customize how our models are viewed in the admin, add and edit model data, and learn how to manage users in the admin.
Accessing the Django Admin Site
When you ran startproject
in Chapter 2, Django created and configured the default admin site for you. All you need to do now is create an admin user (superuser) to log into the admin site. To create an admin user, run the following command from inside your virtual environment:
python manage.py createsuperuser
Enter your desired username and press enter:
Username: admin
Django then prompts you for your email address:
Email address: admin@example.com
The final step is to enter your password. Enter your password twice, the second time to confirm your password:
Password: **********
Password (again): *********
Superuser created successfully.
Now you have created an admin user, you’re ready to use the Django admin. Let’s start the development server and explore.
First, make sure the development server is running, then open a web browser to http://127.0.0.1:8000/admin/
. You should see the admin’s login screen (Figure 7-1).

Figure 7-1: Django’s admin login screen.
Log in with the superuser account you created. Once logged in, you should see the Django admin index page (Figure 7-2).

Figure 7-2: The Django admin index page.
At the top of the index page is the Authentication and Authorization group with two types of editable content: Groups and Users. They are provided by the authentication framework included in Django. We will look at users and groups later in the chapter.
Registering Models With the Admin
There’s not a lot going on here right now because we haven’t registered our app’s models—Venue
, MyClubUser
and Event
—with the admin. Registering and configuring a model for the admin is done by adding the models to the events
app’s admin.py
file (changes in bold):
# \myclub_root\events\admin.py
1 from django.contrib import admin
2 from .models import Venue, MyClubUser, Event
3
4 admin.site.register(Venue)
5 admin.site.register(MyClubUser)
6 admin.site.register(Event)
We’ve added four lines of code to our admin.py
file:
- Line 2. Import the
Venue
,MyClubUser
andEvent
models. - Line 4. Register the
Venue
model with the admin. - Line 5. Register the
MyClubUser
model with the admin. - Line 6. Register the
Event
model with the admin.
Save the file, refresh your browser, and your admin index page should now look like Figure 7-3. You can see there is now an Events group under the Authentication and Authorization group.

Figure 7-3: Once registered, the Venue
, MyClubUser
and Event
models display in the admin.
When you register a model with the admin, you can use the admin to add, edit and delete model records. Now we’ve registered the Event
, MyClubUser
and Venue
models with the admin, we can use the admin to manage venues, users and events for our club.
Django’s admin is designed to be simple and intuitive for non-technical users. Each model registered with the admin has a change list and an edit form. If you click on Venues in the admin index, this will open the venues change list (Figure 7.4). Your venue list might be empty—I have added some records so you can see the default change list for the model. Also note Django versions 3.2 and later add a left navigation menu to the admin. This menu can be expanded and collapsed with the >>
and <<
icons on the center left of your browser screen.

Figure 7-4: The Venues change list. Click “Add Venue” to add a new venue. Django 3.2+ adds a left navigation menu to admin list and detail pages (not shown).
The change list is a table containing all the rows in the database for that particular model. To open an edit form to edit a record, click on the record title (the venue name in this example). To open a blank edit form to add a new record, click the “Add Venue” button on the top right.
Making Fields Optional
Enter Venue Name, Address and Zip/Post Code in the blank form, but leave the Contact Phone, Web Address and Email Address fields blank. Click “Save”. Your screen should look something like Figure 7.5. This error occurred because, by default, all model fields are required.

Figure 7-5: By default, all fields are required when adding records in the admin.
This is not always what you want. For example, a venue might not have a web address, and a venue may provide an email address or a phone number, but not both. To correct this error, we need to update the Venue
model (changes in bold):
# \myclub_root\events\models.py
1 class Venue(models.Model):
2 name = models.CharField('Venue Name', max_length=120)
3 address = models.CharField(max_length=300)
4 zip_code = models.CharField('Zip/Post Code', max_length=12)
5 phone = models.CharField('Contact Phone', max_length=20, blank=True)
6 web = models.URLField('Web Address', blank=True)
7 email_address = models.EmailField('Email Address', blank=True)
8
9 def __str__(self):
10 return self.name
You can see in lines 5, 6 and 7 I have set the blank
field option to True
. The default is False
, which makes the field required.
As we have changed a model, we need to update the database. First, run makemigrations
:
(env_myclub) ...\myclub_root> python manage.py makemigrations
Migrations for 'events':
events\migrations\0003_auto_20200523_0740.py
- Alter field email_address on venue
- Alter field phone on venue
- Alter field web on venue
Then run the migrate
command to migrate the changes to the database:
(env_myclub) ...\myclub_root> python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, events, sessions
Running migrations:
Applying events.0003_auto_20200523_0740... OK
Restart the development server and log in to the admin. Click on the “Add” link next to the Venues listing to open the edit form (Figure 7-6).

Figure 7-6: The venue edit form with optional fields.
The first thing you should notice is that the Contact Phone, Web Address and Email Address field names are no longer in bold text. This indicates these fields are no longer required. Enter some information for Venue Name, Address and Zip/Post Code and click “Save”. You should have no problem entering the new venue with the blank fields. Django will display a success message and switch back to the venue change list view.
Customizing the Venue Change List
The default change list view is not very user-friendly—it just lists the venue names in no particular order. To make the list display more useful, we will make some customizations:
- Show the venue names in alphabetical order to make browsing easier.
- Add the venue address and phone number to the listing to make it easier to access key contact information; and
- Add name and address search capabilities, so once the venue list gets larger, it’s easier to find a particular venue.
In Django, each model is represented in the admin interface by the ModelAdmin
class. To customize how a model displays in the admin, you can set several options in a custom ModelAdmin
subclass. Open your admin.py
file and add a custom VenueAdmin
class (changes in bold):
# \myclub_root\events\admin.py
1 from django.contrib import admin
2 from .models import Venue, MyClubUser, Event
3
4
5 @admin.register(Venue)
6 class VenueAdmin(admin.ModelAdmin):
7 list_display = ('name', 'address', 'phone')
8 ordering = ('name',)
9 search_fields = ('name', 'address')
10
11
12 # admin.site.register(Venue)
13 admin.site.register(MyClubUser)
14 admin.site.register(Event)
Let’s have a closer look at this new class:
- Line 5. We’re using the
register
decorator to register the new class with the admin. Theregister
decorator is functionally equivalent to theregister
method. I am using the decorator here because most books and online tutorials use theregister
method and I wanted to give you an example of an alternative way of registering your customModelAdmin
subclasses. If you wanted to use the register method, you would delete line 5 and replace line 12 withadmin.site.register(Venue, VenueAdmin)
. - Line 6 is your
VenueAdmin
class declaration, which subclasses theadmin.ModelAdmin
class. - Line 7 sets the
list_display
option to show the venue name, address and phone number in the venue change list. - Line 8 sets the default sort order to the venue name. As
ordering
is a 1-tuple (singleton), don’t forget the comma on the end! TIP: If you wanted the default sort to be in reverse order, you can useordering = ('-<fieldname>',)
. In this example, if you wanted to sort in reverse order by venue name it would beordering = ('-name',)
. - Line 9 sets the default search fields to the venue name and venue address.
- Line 12. As we’re using the
register
decorator, line 12 is not needed, so I have commented it out.
If you refresh the admin in your browser, the venue change list should now look like Figure 7-7.

Figure 7-7: The venue admin now has search capability, the venues are listed in alphabetical order, and the venue address and phone number columns have been added to the change list.
Looking at your customized venue change list, you will note that:
- The venues are listed in alphabetical order.
- Each venue’s address and phone number are listed, along with the venue name; and
- A search bar has been added to the change list page. Django will perform a case-insensitive search of the venue name and address fields for whatever term you enter into the search box. To show all records again, search for an empty string.
Customizing the Events Change List and Form
Now, we will make some changes to the events change list and form to make it more user-friendly too. Go back to the admin index by clicking Django administration in the top menu, then click the “Add” link on the right of the Events listing to open the event edit form (Figure 7-8).
The event edit form should be simple enough to follow. Note how the venue field automatically provides you with a drop-down list of all the venues recorded in the database. Django created this link for you when you created the relationship between the Event
model and related records in the venues
model. Also, note how you can create a new venue from the event edit form by clicking the green cross next to the field.

Figure 7-8: The event change form is automatically linked to venue records in the database through model relationships. To add a new venue from the event change form, click the green cross.
There’s nothing wrong with this form, but there are a few things we can tidy up to make it easier to use:
- Link the event manager field to the user database so a staff member can be selected.
- Remove the attendees list from the form; and
- Reorganize the fields on the form, including listing the event name and venue on the same line.
We also want to make the following changes to the event change list:
- Modify the event change list to display additional columns; and
- Add filter by date and filter by venue options to the event change list.
Update the Event Model
To link the Event
model to the user database, we need to make some changes to the model class (changes in bold):
# \myclub_root\events\models.py
1 from django.db import models
2 from django.contrib.auth.models import User
3
4 # ...
5
6 class Event(models.Model):
7 name = models.CharField('Event Name', max_length=120)
8 event_date = models.DateTimeField('Event Date')
9 venue = models.ForeignKey(Venue, blank=True, null=True, on_delete=models.CASCADE)
10 manager = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL)
11 attendees = models.ManyToManyField(MyClubUser, blank=True)
12 description = models.TextField(blank=True)
13
14 def __str__(self):
15 return self.name
In line 2, we’ve imported the User
model from django.contrib.auth.models
. In line 10, we’ve changed the manager
field to a ForeignKey
field that links to the User
model. The on_delete
option is set to SET_NULL
so if a user is deleted, all events they managed have their manager ID set to NULL
.
Once you have modified the model class, create and run the migration.
(env_myclub) ...\myclub_root> python manage.py makemigrations
Migrations for 'events':
events\migrations\0004_auto_20200523_0751.py
- Alter field manager on event
(env_myclub) ...\myclub_root> python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, events, sessions
Running migrations:
Applying events.0004_auto_20200523_0751... OK
Modify the Event Change List and Edit Form
To customize the admin for the Event
model, we will modify admin.py
and add a subclass of ModelAdmin
, just as we did with the Venue
model (changes in bold):
# \myclub_root\events\admin.py
1 from django.contrib import admin
2 from .models import Venue, MyClubUser, Event
3
4
5 @admin.register(Venue)
6 class VenueAdmin(admin.ModelAdmin):
7 list_display = ('name', 'address', 'phone')
8 ordering = ('name',)
9 search_fields = ('name', 'address')
10
11
12 @admin.register(Event)
13 class EventAdmin(admin.ModelAdmin):
14 fields = (('name','venue'), 'event_date', 'description', 'manager')
15 list_display = ('name', 'event_date', 'venue')
16 list_filter = ('event_date', 'venue')
17 ordering = ('-event_date',)
18
19 # admin.site.register(Venue)
20 admin.site.register(MyClubUser)
21 # admin.site.register(Event)
The new EventAdmin
class is basically the same as the VenueAdmin
class, with two notable differences:
- Line 14. The
fields
option lists all the fields to show on the edit form, in the order they are to be displayed. If two or more fields are listed in parentheses, they are grouped together on the same line. Note theattendees
field is not in the field list. To hide a field on an admin form, you simply don’t include it in the fields list. Be careful though—you can’t hide required fields; otherwise, the form won’t save! - Line 16. The
list_filter
option is a tuple of fields to add “Filter by…” filters to the right sidebar of the model change list. - Line 21. Don’t forget to comment out or delete this line as we’re using the
admin.register
decorator.
Figure 7-9 shows the result of the customizations on the edit form and Figure 7-10 the resulting change list.

Figure 7-9: The customized event edit form.

Figure 7-10: The customized event change list.
Grouping Information with Fieldsets
On larger, more complicated forms, it’s easier to manage the form if the form fields are in groups. This is accomplished with the fieldsets
option. An advantage of the fieldsets
option is it provides some additional options that allow you to customize your model edit form further. To demonstrate, let’s make some changes to the EventAdmin
class (changes in bold):
# \myclub_root\events\admin.py
# ...
1 @admin.register(Event)
2 class EventAdmin(admin.ModelAdmin):
3 # fields = (('name','venue'), 'event_date', 'description', 'manager')
4 list_display = ('name', 'event_date', 'venue')
5 list_filter = ('event_date', 'venue')
6 ordering = ('-event_date',)
7 fieldsets = (
8 ('Required Information', {
9 'description': "These fields are required for each event.",
10 'fields': (('name','venue'), 'event_date')
11 }),
12 ('Optional Information', {
13 'classes': ('collapse',),
14 'fields': ('description', 'manager')
15 }),
16 )
Let’s have a closer look at the changes:
- Line 3. I’ve commented out the
fields
option for the class. This is because the form field display and layout options are now defined in thefieldsets
option. - Lines 7 to 16 is the new
fieldset
. Afieldset
is a list of 2-tuples, each of which represents a group of fields on the edit form. Each 2-tuple includes a group name and a dictionary of field options:- Line 8. The first fieldset is named “Required Information”.
- Line 9. The first option sets the description for the group.
- Line 10. This has the same effect as the fields option we commented out:
name
andvenue
are listed on the same line, andevent_date
is added to complete the “Required Information” group. - Line 12. The second fieldset is named “Optional Information”.
- Line 13. Adds the
collapse
class to the fieldset. This will apply a JavaScipt accordion-style to collapse the fieldset when the form first displays. - Line 14. The remaining two form fields—
description
andmanager
—are displayed as a part of the “Optional Information” group.
Save the admin.py
file and reload the admin and you should find that the event edit form looks like Figure 7-11. Note the required and optional group names, the description for the required group and the hidden optional fields when the form loads. Click “Show” next to the optional information title and the extra fields will show.

Figure 7-11: The event edit form with fieldset options set.
Grouping fields like this on such a simple form is arguably overkill, but it demonstrates how Django’s highly configurable model admin can make managing form data easier.
I have only covered about half of the options and methods available for the ModelAdmin
class in this chapter. If you want to explore the remaining options and methods for the class, the Django documentation is a great reference.
Other admin options you want to check out are:
date_hierarchy
. Provides a date-based drilldown for the field.empty_value_display
. Changes how empty values are displayed.exclude
. Excludes a field or fields from the formfilter_horizontal
. Adds a JavaScript horizontal filter for many-to-many fields. If you want to see the horizontal filter in action, see user permissions and groups in the next section.list_editable
. Makes the change list fields editable.readonly_fields
. Sets the listed form fields to read-only. Very handy for timestamp fields and other fields you want visible, but not editable.

Like the Content? Grab the Book for as Little as $14!
Other than providing you with a convenient resource you can print out, or load up on your device, buying the book also helps support this site.
eBook bundle includes PDF, ePub and source and you only pay what you can afford.
You can get the paperback from Amazon.
Managing Users in the Admin
If you remember from the beginning of the chapter, Django’s built-in authentication system is added to the admin interface when you create a new project. With the admin you can:
- Add and delete users
- Edit existing users
- Reset user passwords
- Assign staff and/or superuser status to a user
- Add or remove user permissions
- Create user groups; and
- Add users to a group
The superuser we created earlier has full access to all models in the admin and can add, change, and delete any model record. In a real application, you will want to limit the number of users who have full access to your site.
Adding a new user is easy—go to the admin index page and click the green plus sign on the right of the Users entry on the admin home page. Enter a username and password and click save to add the new user.
Return to the admin home page and click Users to open the user change list (Figure 7-12). Click on the username to open the user edit screen.

Figure 7-12: Select the new user from the user change list to edit the user’s details.
At the top of the user edit screen, you will see options to edit the user’s password and personal info. Scroll down to the Permissions section and make sure “Staff status” is checked and “Superuser status” is unchecked (Figure 7-13).

Figure 7-13: Create a normal admin user (non-superuser) by making sure they are active and have staff status, but don’t have superuser status.
What we have created here is considered a normal admin user. Normal admin users (active, non-superuser staff members) are granted admin access through assigned permissions. Each object editable through the admin interface (e.g., events and venues) has four permissions: a create permission, a view permission, an edit permission, and a delete permission.
Assigning permissions to a user grants the user access to do what is described by those permissions. When you create a user, they have no permissions. It’s up to you to give the user-specific permissions.
We will do that now—we will create a staff user who has permission to add and edit events, but not to delete them. Scroll down the edit page to the User permissions panel and add the following permissions using the horizontal filter (Figure 7-14):
events | event | Can add event
events | event | Can change event

Figure 7-14: Add permissions to the user by selecting in the horizontal filter and adding to the list. Make multiple selections by holding down the CTRL key (Command on a Mac).
Once you have added the permissions, log out and log back in as the new user. The admin dashboard will now only show the events
app, hiding all the other models that the user doesn’t have permission to access (Figure 7-15).

Figure 7-15: The new user’s permission setting limits their admin access to the events app. If you open an event, you will also notice the delete button is hidden as they don’t have delete permission.
This is pretty straightforward, but what if you have many staff members who need permission to add and edit events? It’s time-consuming to add permissions one at a time to each user. Luckily, Django allows you to create user groups, which is simply a group of permissions that can be added to a user simultaneously, rather than one at a time.
Let’s create an “Event Admin” group. You will first have to log out as the staff user and log back in as the superuser.
Creating a group is like creating a user: go to the admin index page, click the green Add button to the right of the Groups listing, and name your new group “Event Admin”. Then, add the permissions from the horizontal filter and save your new group (Figure 7-16).

Figure 7-16: Create a user group and add event add and change permissions to the group using the horizontal filter. Make multiple selections by holding down the CTRL key (Command key on a Mac).
Once you have added the group, you can go back to the user and edit their permissions to add the new group (Figure 7-17).

Figure 7-17: Adding a user to a group assigns all the group’s permissions to the user.
Don’t forget to delete the permissions you assigned previously to prevent any permission clashes later. Save the user, and now, when you log out and log back in again as the staff user, they will have the same restricted view of the admin as we saw in Figure 7-15.
Changing Passwords
As a security measure, Django doesn’t store raw passwords, so it’s impossible to retrieve a password. A user can change their password, but they have to be logged in first.
So how do you reset a password if the user has forgotten it?
The default admin configuration only allows an admin, or someone with permission to edit users, to reset a password by using the password reset form link on the user edit form (Figure 7-18).

Figure 7-18: Default way to reset a user’s password.
Obviously, we don’t want to require an admin to log in and manually reset user passwords each time someone forgets their password.
Giving staff users permission to edit a user record is not practical either because giving anyone edit user permissions will allow them to edit all users (effectively turning them into a superuser).
Thankfully, Django has a password reset feature built in; we just have to turn it on. Make the following modifications to your site urls.py
file (changes in bold):
# \myclub_root\myclub_site\urls.py
1 from django.contrib import admin
2 from django.urls import include, path
3 from django.contrib.auth import views as auth_views
4
5 urlpatterns = [
6 path('admin/', admin.site.urls),
7 path(
8 'admin/password_reset/',
9 auth_views.PasswordResetView.as_view(),
10 name='admin_password_reset',
11 ),
12 path(
13 'admin/password_reset/done/',
14 auth_views.PasswordResetDoneView.as_view(),
15 name='password_reset_done',
16 ),
17 path(
18 'reset/<uidb64>/<token>/',
19 auth_views.PasswordResetConfirmView.as_view(),
20 name='password_reset_confirm',
21 ),
22 path(
23 'reset/done/',
24 auth_views.PasswordResetCompleteView.as_view(),
25 name='password_reset_complete',
26 ),
27 path('', include('events.urls')),
28 ]
On line 3, we import the authentication views from django.contrib.auth
and add four new path statements to our site URLs (lines 7, 12, 17, and 22). Once Django detects a URL named admin_password_reset
, it will automatically add a password reset link to the login form (Figure 7-19).

Figure 7-19: Adding the authentication views to your site URLs enables the password reset feature of the login form.
Note we’ve only enabled the link, you would need to set up an email backend for the reset link to actually work.
Chapter Summary
In this chapter, we learned how to access the Django admin, register models with the admin, add and edit model records, customize the look of model change lists and edit forms in the admin, and how to manage users in the admin.
In the next chapter, we will learn the fundamentals of forms and form management in Django.