Published on

How to add Custom Filters to Django Admin? Also...

Overview

In this tutorial we'll cover following django admin customizations.

  • Adding custom filters along with normal filters on your django admin table.
  • Adding customize action button on admin table.
  • Adding additional button on ModelAdmin form.

Custom Filters in Django admin

Django does a great job providing filters by default according to your model fields. But what if you need to add custom filter that can filter records according to your custom logic. Then you need to implement a custom filter and Django provide this capability out of the box.

Let's consider a table that store Starwars characters having mass as one of the fields and you want to filter the characters by mass category for example, light, heavy, super heavy etc.

@admin.register(models.Characters)
class CharactersAdmin(ImportExportModelAdmin, admin.ModelAdmin):
    list_display = ("name", "birth_year", "gender", "mass")
    list_filter = (WeightFilter, "gender", "hair_color")

    ...

Then you have to implement a class with name WeightFilter in the following way.

class WeightFilter(admin.SimpleListFilter):
    title = "By Weight"
    parameter_name = "weight"

    def lookups(self, request, model_admin):
        return [('light', 'Light'), ('heavy', 'Heavy'), ('super_heavy', 'Super Heavy')]

    def queryset(self, request, queryset):
        if self.value() == 'light':
            return queryset.filter(mass__lte=50.0)
        if self.value() == 'heavy':
            return queryset.filter(mass__range=(51.0, 100.0))
        if self.value() == 'super_heavy':
            return queryset.filter(mass__gte=101.0)
        return queryset

The final result would look something like this.

SQL Editor

Custom Action Buttons on Django admin.

You can easily customize your admin to add custom button at top right corner beside Add object button. You can also add url to those buttons and perform some action on them. Let's see how to do it.

Add the following file in your templates.

templates/admin/characters_changelist.html
{% extends "admin/change_list.html" %}
{% load i18n static %}
{% block object-tools-items %}
<li>
    <a href="attack/">Attack</a>
</li>
<li>
    <a href="defend/">Defend</a>
</li>

{{ block.super }}
{% endblock %}

Then in your admin class add following methods.

@admin.register(models.Characters)
class CharactersAdmin(ImportExportModelAdmin, admin.ModelAdmin):
    list_display = ("name", "birth_year", "gender", "mass")
    list_filter = (WeightFilter, "gender", "hair_color")

    change_list_template = "admin/characters_changelist.html"

    def get_urls(self):
        # additional urls
        from django.urls import path
        urls = super().get_urls()
        my_urls = [
            path('attack/', self.attack),
            path('defend/', self.defend),
        ]
        return my_urls + urls

    def attack(self, request):
        # custom operation
        print("attack")
        return HttpResponseRedirect("../")

    def defend(self, request):
        # custom operation
        print("defend")
        return HttpResponseRedirect("../")

    ...

The final result would look something like this.

SQL Editor

Additional Buttons in ModelAdmin form.

Now let's say you need add custom buttons at the end regular model form to have extra functionaly then you can customize change_form_template like following.

templates/admin/characters_change_form.html
{% extends "admin/change_form.html" %}
{% load admin_modify %}
{% load i18n admin_urls%}

{% block submit_buttons_bottom %}
{% submit_row %}
<input type="submit" value="Power Up" name="_powerup" />
<input type="submit" value="Heal" name="_heal" />
{% endblock %}

Then to listen to submit events, head over to your admin class.

@admin.register(models.Characters)
class CharactersAdmin(ImportExportModelAdmin, admin.ModelAdmin):
    list_display = ("name", "birth_year", "gender", "mass")
    list_filter = (WeightFilter, "gender", "hair_color")

    change_form_template = "admin/characters_change_form.html"

    def save_model(self, request, obj, form, changed):

        if '_powerup' in request.POST:
            print("Power Up")
            messages.success(request, "Powered Up successfully!")

        if '_heal' in request.POST:
            print("Heal")
            messages.success(request, "Healed successfully!")

        obj.save()

    ...

The final result would look something like this.

SQL Editor

Conclusion

We saw how can we easily customize few aspects of django admin and make our lives easier while performing admin operations. I hope you have learned some new things from this article and if you do then please drop a like and share with your colleagues and friends. See you in the next one 👋!