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.

Customizing the Events Change List and Form - 图1

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

  1. # \myclub_root\events\models.py
  2. 1 from django.db import models
  3. 2 from django.contrib.auth.models import User
  4. 3
  5. 4 # ...
  6. 5
  7. 6 class Event(models.Model):
  8. 7 name = models.CharField('Event Name', max_length=120)
  9. 8 event_date = models.DateTimeField('Event Date')
  10. 9 venue = models.ForeignKey(Venue, blank=True, null=True, on_delete=models.CASCADE)
  11. 10 manager = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL)
  12. 11 attendees = models.ManyToManyField(MyClubUser, blank=True)
  13. 12 description = models.TextField(blank=True)
  14. 13
  15. 14 def __str__(self):
  16. 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.

  1. (env_myclub) ...\myclub_root> python manage.py makemigrations
  2. Migrations for 'events':
  3. events\migrations\0004_auto_20200523_0751.py
  4. - Alter field manager on event
  5. (env_myclub) ...\myclub_root> python manage.py migrate
  6. Operations to perform:
  7. Apply all migrations: admin, auth, contenttypes, events, sessions
  8. Running migrations:
  9. 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):

  1. # \myclub_root\events\admin.py
  2. 1 from django.contrib import admin
  3. 2 from .models import Venue, MyClubUser, Event
  4. 3
  5. 4
  6. 5 @admin.register(Venue)
  7. 6 class VenueAdmin(admin.ModelAdmin):
  8. 7 list_display = ('name', 'address', 'phone')
  9. 8 ordering = ('name',)
  10. 9 search_fields = ('name', 'address')
  11. 10
  12. 11
  13. 12 @admin.register(Event)
  14. 13 class EventAdmin(admin.ModelAdmin):
  15. 14 fields = (('name','venue'), 'event_date', 'description', 'manager')
  16. 15 list_display = ('name', 'event_date', 'venue')
  17. 16 list_filter = ('event_date', 'venue')
  18. 17 ordering = ('-event_date',)
  19. 18
  20. 19 # admin.site.register(Venue)
  21. 20 admin.site.register(MyClubUser)
  22. 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 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.

Customizing the Events Change List and Form - 图2

Figure 7-9: The customized event edit form.

Customizing the Events Change List and Form - 图3

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

  1. # \myclub_root\events\admin.py
  2. # ...
  3. 1 @admin.register(Event)
  4. 2 class EventAdmin(admin.ModelAdmin):
  5. 3 # fields = (('name','venue'), 'event_date', 'description', 'manager')
  6. 4 list_display = ('name', 'event_date', 'venue')
  7. 5 list_filter = ('event_date', 'venue')
  8. 6 ordering = ('-event_date',)
  9. 7 fieldsets = (
  10. 8 ('Required Information', {
  11. 9 'description': "These fields are required for each event.",
  12. 10 'fields': (('name','venue'), 'event_date')
  13. 11 }),
  14. 12 ('Optional Information', {
  15. 13 'classes': ('collapse',),
  16. 14 'fields': ('description', 'manager')
  17. 15 }),
  18. 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 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.

Customizing the Events Change List and Form - 图4

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.