Mastering Django: Admin

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 createsuperuser

Enter your desired username and press enter:

Username: admin

Django then prompts you for your email address:

Email address:

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 You should see the admin’s login screen (Figure 7-1).

django admin login

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).

django admin index

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 file (changes in bold):

# \myclub_root\events\

1  from django.contrib import admin
2  from .models import Venue, MyClubUser, Event

We’ve added four lines of code to our file:

  • Line 2. Import the Venue, MyClubUser and Event 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.

django admin index with models

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.

django admin change list

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.

required fields

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\

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)
9      def __str__(self):
10        return

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 makemigrations
Migrations for 'events':
    - 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 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:

  1. Show the venue names in alphabetical order to make browsing easier.
  2. Add the venue address and phone number to the listing to make it easier to access key contact information; and
  3. 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 file and add a custom VenueAdmin class (changes in bold):

# \myclub_root\events\

1  from django.contrib import admin
2  from .models import Venue, MyClubUser, Event
5  @admin.register(Venue)
6  class VenueAdmin(admin.ModelAdmin):
7      list_display = ('name', 'address', 'phone')
8      ordering = ('name',)
9      search_fields = ('name', 'address')
12 #

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. The register decorator is functionally equivalent to the register method. I am using the decorator here because most books and online tutorials use the register method and I wanted to give you an example of an alternative way of registering your custom ModelAdmin subclasses. If you wanted to use the register method, you would delete line 5 and replace line 12 with, VenueAdmin).
  • Line 6 is your VenueAdmin class declaration, which subclasses the admin.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 use ordering = ('-<fieldname>',). In this example, if you wanted to sort in reverse order by venue name it would be ordering = ('-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.

custom change list

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:

  1. The venues are listed in alphabetical order.
  2. Each venue’s address and phone number are listed, along with the venue name; and
  3. 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.

admin edit form

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:

  1. Link the event manager field to the user database so a staff member can be selected.
  2. Remove the attendees list from the form; and
  3. 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:

  1. Modify the event change list to display additional columns; and
  2. 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\

1  from django.db import models
2  from django.contrib.auth.models import User
4  # ...
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)
14     def __str__(self):
15         return

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 makemigrations
Migrations for 'events':
    - Alter field manager on event
(env_myclub) ...\myclub_root> python 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 and add a subclass of ModelAdmin, just as we did with the Venue model (changes in bold):

# \myclub_root\events\

1  from django.contrib import admin
2  from .models import Venue, MyClubUser, Event
5  @admin.register(Venue)
6  class VenueAdmin(admin.ModelAdmin):
7      list_display = ('name', 'address', 'phone')
8      ordering = ('name',)
9      search_fields = ('name', 'address')
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',)
19 #
21 #

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 the attendees 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\

# ...

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 the fieldsets option.
  • Lines 7 to 16 is the new fieldset. A fieldset 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 and venue are listed on the same line, and event_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 and manager—are displayed as a part of the “Optional Information” group.

Save the 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.

edit form with fieldsets

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 form
  • filter_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.
MDJ32 web cover

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.

Get the eBook bundle here.

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.

user list

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).

make user a staff member

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
add user permissions

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).

add user group

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).

add user to group

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).

default password reset

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 file (changes in bold):

# \myclub_root\myclub_site\

1  from django.contrib import admin
2  from django.urls import include, path
3  from django.contrib.auth import views as auth_views
5  urlpatterns = [
6      path('admin/',,
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).

password reset

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.

Up Next…

Scroll to Top