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 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.
- 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.
Delete All Event Records First!
Make sure you have no event records in the database, or the migration will fail!
This is because you are changing the manager
field to a foreign key and the migration will break when the data in the field doesn’t match a user ID.
(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.
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.