Let’s dive into admin customization by specifying the fields that are displayed on the change list for our
Author model. By default, the change list displays the result of
__str__() for each object. In Chapter 4, we defined the
__str__() method for
Author objects to display the first name and last name together:
As a result, the change list for
Author objects displays each other’s first name and last name together, as you can see in Figure 5-7.
We can improve on this default behavior by adding a few other fields to the change list display. It’d be handy, for example, to see each author’s e-mail address in this list, and it’d be nice to be able to sort by first and last name. To make this happen, we’ll define a
ModelAdmin class for the
Author model. This class is the key to customizing the admin, and one of the most basic things it lets you do is specify the list of fields to display on change list pages. Edit
admin.py to make these changes:
Here’s what we’ve done:
- We created the class
AuthorAdmin. This class, which subclasses
django.contrib.admin.ModelAdmin, holds custom configuration for a specific admin model. We’ve only specified one customization –
list_display, which is set to a tuple of field names to display on the change list page. These field names must exist in the model, of course.
- We altered the
admin.site.register()call to add
Author. You can read this as: “Register the
Authormodel with the
admin.site.register()function takes a
ModelAdminsubclass as an optional second argument. If you don’t specify a second argument (as is the case for
Book), Django will use the default admin options for that model.
With that tweak made, reload the author change list page, and you’ll see it’s now displaying three columns – the first name, last name and e-mail address. In addition, each of those columns is sortable by clicking on the column header. (See Figure 5-8.)
Next, let’s add a simple search bar. Add
search_fields to the
AuthorAdmin, like so:
Reload the page in your browser, and you should see a search bar at the top. (See Figure 5-9.) We’ve just told the admin change list page to include a search bar that searches against the
last_name fields. As a user might expect, this is case-insensitive and searches both fields, so searching for the string “
bar” would find both an author with the first name Barney and an author with the last name Hobarson.
Next, let’s add some date filters to our
Book model’s change list page:
Here, because we’re dealing with a different set of options, we created a separate
ModelAdmin class –
BookAdmin. First, we defined a
list_display just to make the change list look a bit nicer. Then, we used
list_filter, which is set to a tuple of fields to use to create filters along the right side of the change list page. For date fields, Django provides shortcuts to filter the list to “Today,” “Past 7 days,” “This month” and “This year” – shortcuts that Django’s developers have found hit the common cases for filtering by date. Figure 5-10 shows what that looks like.
list_filter also works on fields of other types, not just
DateField. (Try it with
ForeignKey fields, for example.) The filters show up as long as there are at least 2 values to choose from. Another way to offer date filters is to use the
date_hierarchy admin option, like this:
With this in place, the change list page gets a date drill-down navigation bar at the top of the list, as shown in Figure 5-11. It starts with a list of available years, then drills down into months and individual days.
date_hierarchy takes a string, not a tuple, because only one date field can be used to make the hierarchy. Finally, let’s change the default ordering so that books on the change list page are always ordered descending by their publication date. By default, the change list orders objects according to their model’s
class Meta (which we covered in Chapter 4) – but you haven’t specified this
ordering value, then the ordering is undefined.
ordering option works exactly as the
ordering in models’
class Meta, except that it only uses the first field name in the list. Just pass a list or tuple of field names, and add a minus sign to a field to use descending sort order. Reload the book change list to see this in action. Note that the “Publication date” header now includes a small arrow that indicates which way the records are sorted. (See Figure 5-12.)
We’ve covered the main change list options here. Using these options, you can make a very powerful, production-ready data-editing interface with only a few lines of code.
Customizing Edit Forms
Just as the change list can be customized, edit forms can be customized in many ways. First, let’s customize the way fields are ordered. By default, the order of fields in an edit form corresponds to the order they’re defined in the model. We can change that using the
fields option in our
After this change, the edit form for books will use the given ordering for fields. It’s slightly more natural to have the authors after the book title. Of course, the field order should depend on your data-entry workflow. Every form is different.
Another useful thing the
fields option lets you do is to exclude certain fields from being edited entirely. Just leave out the field(s) you want to exclude. You might use this if your admin users are only trusted to edit a certain segment of your data, or if some of your fields are changed by some outside, automated process.
For example, in our book database, we could hide the
publication_date field from being editable:
As a result, the edit form for books doesn’t offer a way to specify the publication date. This could be useful, say, if you’re an editor who prefers that his authors not push back publication dates. (This is purely a hypothetical example, of course.) When a user uses this incomplete form to add a new book, Django will simply set the
None – so make sure that field has
Another commonly used edit-form customization has to do with many-to-many fields. As we’ve seen on the edit form for books, the admin site represents each
ManyToManyField as a multiple-select boxes, which is the most logical HTML input widget to use – but multiple-select boxes can be difficult to use. If you want to select multiple items, you have to hold down the control key, or command on a Mac, to do so.
The admin site helpfully inserts a bit of text that explains this, but it still gets unwieldy when your field contains hundreds of options. The admin site’s solution is
filter_horizontal. Let’s add that to
BookAdmin and see what it does.
(If you’re following along, note that we’ve also removed the
I’d highly recommend using
filter_horizontal for any
ManyToManyField that has more than 10 items. It’s far easier to use than a simple multiple-select widget. Also, note you can use
filter_horizontal for multiple fields – just specify each name in the tuple.
ModelAdmin classes also support a
filter_vertical option. This works exactly as
filter_vertical only work on
ManyToManyField fields, not
ForeignKey fields. By default, the admin site uses simple
<select> boxes for
ForeignKey fields, but, as for
ManyToManyField, sometimes you don’t want to incur the overhead of having to select all the related objects to display in the drop-down.
For example, if our book database grows to include thousands of publishers, the “Add book” form could take a while to load, because it would have to load every publisher for display in the
The way to fix this is to use an option called
Set this to a tuple of
ForeignKey field names, and those fields will be displayed in the admin with a simple text input box (
<input type="text">) instead of a
<select>. See Figure 5-14.
What do you enter in this input box? The database ID of the publisher. Given that humans don’t normally memorize database IDs, there’s also a magnifying-glass icon that you can click to pull up a pop-up window, from which you can select the publisher to add.