The Django Admin - Python Django Tutorials

The Django Admin

A web-based administrative backend 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’re going to 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 that 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

You will then be prompted for your email address:

Email address: admin@example.com

The final step is to enter your password. You will be asked to enter your password twice, the second time as a confirmation of the first.

Password: **********
Password (again): *********
Superuser created successfully.

Now that you have created an admin user, you’re ready to start using 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.

Since translation is turned on by default, the login screen may be displayed in your own language, depending on your browser’s settings and whether Django has a translation for your language.

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 be looking at users and groups a bit later in the chapter.

Registering Models with the Admin

There’s not a lot going on here right new because we haven’t registered our three 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 three lines of code to our admin.py 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. Note that you now have an Events group under the Authentication and Authorization group.

Figure 7-3: Once registered, the Venue, MyclubUser and Event models show in the admin.

When a model is registered with the admin, you can use the admin to add, edit and delete model records. Now we’ve registered the Event and Venue models with the admin, we can use the admin to manage venues 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 a couple of records so you can see the default change list for the model.

Figure 7-4: The Venues change list. Click “Add Venue” to add a new venue.

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 a venue name, address and zip 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 occured 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 it’s possible for a venue to supply an email address or a phone number, but not both. To correct this error, we need to make changes to 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 made changes to a model, we need to update the database. First, run makemigrations:

(env_myclub) PS ...\myclub_root> python manage.py makemigrations
Migrations for 'events':
events\migrations\0003_auto_20190527_0737.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) PS ...\myclub_root> python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, events, sessions
Running migrations:
Applying events.0003_auto_20190527_0737... OK

Restart the development server and log back 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 they’re no longer required fields. Enter some information for venue name, address and zip 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

You will notice that 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’re going to 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. We’re going to 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 is displayed 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. 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 admin.site.register(Venue, 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 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.

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

Customizing the Events Change List and Form

Now, we’re going to 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 adminstration 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 automatically when you created the relationship between the Event model and related records in the venues model. Note also that you can create a new venue directly 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 directly 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
  3. Reorganize the fields on the form, including listing the event name and venue on the same line
  4. Modify the event change list to display additional columns
  5. 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 a couple of 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, on_delete=models.CASCADE)
10     manager = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL)
11     attendees = models.ManyToManyField(MyclubUser)
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 and 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) PS ...\myclub_root> python manage.py makemigrations
Migrations for 'events':
events\migrations\0004_auto_20190527_1106.py
    - Alter field manager on event
    
(env_myclub) PS ...\myclub_root> python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, events, sessions
Running migrations:
Applying events.0004_auto_20190527_1106... OK

Customize the Event Change List and Edit Form

To customize the admin for the Event model, we’re going to 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, 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(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 that are to be shown on the edit form, in the order they are to be displayed. If two or more fields are listed in brackets, they are grouped together on the same line. Note that 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.

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 grouped together. This is accomplished with the fieldsets option. An advantage of the fieldsets option is that it provides some addtional options that allow you to further customize your model edit form. 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 the fieldsets option
  • Lines 7 to 16 is the new fieldset. A fieldset is a list of two-tuples, each of which represents a group of fields on the edit form. Each two-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” 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 that will make the fieldset appear collapsed when the form first displays
    • Line 14. The remaining two form fields—description and manager—are displayed as a part of the “optional” 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 does demonstrate how Django’s highly configurable model admin can be used to make managing form data easier.

I have only covered about half of the options and methods available for the ModelAdmin class in this chapter. I will be covering admin actions in detail in Chapter [TODO], but if you want to explore the remaining options and methods for the class, the Django documentation is a great reference.

Other admin options you definitely want to check out are:

  • date_hierarchy. Provide date-based drilldown for the field.
  • empty_value_display. Change how empty values are displayed.
  • exclude. Exclude a field from the form.
  • filter_horizontal. Add 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. Make the change list fields editable.
  • readonly_fields. Set form fields to read only. Very handy for timestamp fields and other fields you want shown, but not editable.

Managing Users in the Admin

If you remember from the beginning of the chapter, Django’s built-in authentication system is automatically 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: elect 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—that is, 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, that user has no permissions. It’s up to you to give the user specific permissions.

We’re going to do that now—we are going to 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. Multiple selections can be made 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 any event, you will also notice the delete button is hidden as they don’t have delete permission.

This is pretty straight forward, 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 go ahead and 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 and click the green add button to the right of the Groups listing and name your new group “Event Admin”, add the permissions from the horizontal filter and save your new group (Figure 7-16).

Figure 7-16: Create a user group and add permissions to the group using the horizontal filter. Multiple selections can be made by holding down the CTRL key (Command 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—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 then 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.

Chapter Summary

In this chapter,we learned how to access the Django admin, register models with the admin, add and edit model data, 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’re going to learn the fundamentals of forms and form management in Django.