Improving the user experience

While we have improved upon the base admin interface quite a bit just by showing the relevant fields on the list page, we can do more. Let's look at some actions that an administrator might want to take for the booking enquiries that the site receives:

  • Viewing only the booking enquiries that have been approved or the ones that have not yet been approved
  • Searching for a booking by customer name
  • Approving or not approving a booking enquiry quickly
  • Selecting multiple booking enquiry objects and sending e-mails to the customers about their approval/disapproval

Filtering objects

For our first feature, we want to allow the user filtering on the displayed objects. There should be a filter on the page that allows them to see only bookings that have been approved or not approved. To do so, the Django admin provides you with a list_filter attribute on the ModelAdmin subclass. The list_filter attribute holds a list of fields that you can filter on. In our BookingModelAdmin class, add the following list_filter attribute:

list_filter = ['is_approved']

That's it. Once you have added this line to BookingModelAdmin, open up the bookings list page; on the right-hand side, you should see a new sidebar where you can select which bookings you want to view—only those that are approved or those that are not approved, or both. It should look similar to the following screenshot:

Filtering objects

Searching for objects

Just as the Django admin has built-in support for filters, it also provides an easy-to-use way to add search. We want our client to be able to search bookings by the customer name field. To do so, add the search_fields attribute to the BookingModelAdmin class:

search_fields = ['customer_name']

That's it. Once you have added this attribute, you should see a search box at the top of the booking objects list. Type in a few sample queries and see how it works. If you have more than one field that you want to make searchable, you can add that to the list of search_fields as well.

If you have more than one field name in the list, Django will do an OR search. This simply means that for a given search, ALL records that have at least ONE matching field value will show.

Quick edit

The third feature on our list is to allow admins to quickly mark a booking as approved/not approved. The Django admin provides another built-in feature that we can configure to get what we need. In your BookingModelAdmin class, add the list_editable attribute:

list_editable = ['is_approved']

If you open the booking list page now, you will notice that instead of the icon that was shown in the is_approved column before, you now have a checkbox and a Save button added at the end of the list. You can select the checkboxes for bookings you want to approve and deselect it for the ones you want to disapprove, and click on Save. Django will then save your changes to multiple objects in one go.

By now, our Booking list page looks similar to the following screenshot:

Quick edit

Admin actions

The last thing on our list of features is the ability for the user to select multiple booking enquiry objects and send out an e-mail to customer_email for each Booking object containing the approval status of the booking. For now, we will just print out the e-mail on the console to test this feature out. We will look at sending e-mails from Django in a later chapter.

Until now, most of the editing that we did with the Django admin was on a per object basis. You select an object, edit it, then save it, and start over. Except for the last feature (quick edit), we have been editing objects one at a time. However, sometimes you want the ability to perform a common action on multiple objects, like we need with our e-mail feature. To implement features like these, the Django admin provides Admin Actions.

Admin actions are methods on the ModelAdmin class that are passed a list of objects that the user has selected. These methods can then take some action on each of these objects and return the user to the changelist page again.

Note

Actually, I am simplifying this a bit. Admin actions don't need to be methods on ModelAdmin. They can be standalone functions as well. However, it's usually a good programming practice to declare them in ModelAdmin that uses them, so that's how we will do it here. You can find more details in the documentation for admin actions at https://docs.djangoproject.com/en/stable/ref/contrib/admin/actions/.

The Django admin by default provides one action: delete. If you open the Action drop-down at the top of the list of bookings, you should see this menu:

Admin actions

To define an admin action, you first need to create a method on the ModelAdmin class and then add the name of the method to the actions attribute on the class. The actions attribute is a list like all the other attributes that we have seen till now. Modify the BookingModelAdmin to match the following code:

class BookingModelAdmin(admin.ModelAdmin):
    list_display = ['customer_name', 'booking_start_date', 'booking_end_date', 'is_approved']
    list_filter = ['is_approved']
    list_editable = ['is_approved']
    search_fields = ['customer_name']

    actions = ['email_customers']

    def email_customers(self, request, queryset):
        for booking in queryset:
            if booking.is_approved:
                email_body = """Dear {},
    We are pleased to inform you that your booking has been approved.
Thanks
""".format(booking.customer_name)
            else:
                email_body = """Dear {},
    Unfortunately we do not have the capacity right now to accept your booking.
Thanks
""".format(booking.customer_name)

            print(email_body)

Let's try it out before looking into what the code does. Refresh the changelist page for the Booking model and look at the Action drop-down. It should have a new option, Email customers:

Admin actions

To test it out, select some booking objects from the list, select the Email customer action from the drop-down menu, and click on the Go button next to the drop-down. After the page loads, look at your console. You should see something similar to what's shown here:

Dear Jibran,
    We are pleased to inform you that your booking has been approved.
Thanks

[18/Jan/2016 09:58:05] "POST /admin/frontend/booking/ HTTP/1.1" 302 0

Let's look at what we've done here. As I said before, an admin action is just a method on the ModelAdmin class that accepts a request object and queryset as parameters, and then performs the required operation on queryset. Here, we create an e-mail body text for each booking object and print it to the console.

UX improvements

While the system is now good enough for our client to use, there is definitely room for improvement. For a start, the user isn't given any feedback whether the Email customers action was performed. Let's fix this first. Add this line to the end of the email_customers method:

self.message_user(request, 'Emails were send successfully')

Try using the e-mail action again. Now when the page reloads, you see a nice success message that assures the user that the action they wanted was completed. Small improvements in UX go a long way in helping the user navigate and successfully use your product.

Secondly, let's look at naming the action. For this action, Django comes up with a pretty good name—Email customers. It's simple and to the point. However, it's not as clear as it should be. It doesn't convey to the user what e-mail is being sent. In a larger system, the client could potentially send many types of e-mails, and our action name should be clear about which e-mail we are talking about.

In order to change the name of the admin action, we need to give the method an attribute called short_description. As methods are also objects in Python, this is pretty easy. Change the BookingModelAdmin class to match the following code. The new line to add is highlighted:

class BookingModelAdmin(admin.ModelAdmin):
    list_display = ['customer_name', 'booking_start_date', 'booking_end_date', 'is_approved']
    list_filter = ['is_approved']
    list_editable = ['is_approved']
    search_fields = ['customer_name']

    actions = ['email_customers']

    def email_customers(self, request, queryset):
        for booking in queryset:
            if booking.is_approved:
                email_body = """Dear {},
    We are pleased to inform you that your booking has been approved.
Thanks
""".format(booking.customer_name)
            else:
                email_body = """Dear {},
    Unfortunately we do not have the capacity right now to accept your booking.
Thanks
""".format(booking.customer_name)

            print(email_body)

        self.message_user(request, 'Emails were send successfully')
    email_customers.short_description = 'Send email about booking status to customers'

Note that the new line (the last one) is not part of the function body. It's indented at the same level as the function definition and is actually part of the class rather than the function. Refresh the list page and take a look at the action drop-down again:

UX improvements
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.227.183.153