, Johann Schmitz

This article is part of the Customizing the Django admin application series:

  1. Django Admin customizing
  2. Django Admin customizing - part 2
  3. Django Admin customizing - part 3

As we have seen the last time adding a quick filter to the django admin application is as easy as adding a model field to the list_filter field. However, only a few fields (those with a few unique values) will produce a useful result. Let's pretend that we want to filter our country list by the population of the countries. If we throw the model field population into list_filter we get a list of all unique values from our Country model, which is quite unusable.

Custom list filter

Fortunatly, more complex filter can be implemented by subclassing one of the various base classes in django.contrib.admin.filters. For this example we'll use the SimpleListFilter class:

class PopulationListFilter(admin.SimpleListFilter):
    title = 'population'
    parameter_name = 'population'

The title field is the title of the filter; the parameter_name is the name of the the URL parameter which carries our select filter.

To do something useful, we have to implement to methods:

  • lookups(self, request, model_admin): Should return a list of tuples of (value, display text)
  • queryset(self, request, queryset): gets called after selecting an item from the filter and should apply a filter to the queryset

Here is my implementation:

class PopulationListFilter(admin.SimpleListFilter):
    title = 'population'
    parameter_name = 'population'

    def lookups(self, request, model_admin):
        max_population = model_admin.queryset(request).aggregate(max_population=Max('population')).get('max_population')

        i = 1
        while (10 ** i) < max_population:
            yield (i-1, '%s to %s' % (10 ** (i-1), 10 ** i))
            i += 1

    def queryset(self, request, queryset):
        if self.value():
            i = int(self.value())
            return queryset.filter(population__gte=10 ** i, population__lte=10 ** (i + 1))
        return queryset

The lookup() function yields items for each power of 10 up to the greatest value of the the population column in the table.

The queryset() function applies a gte and lte filter onto the field to narrow the result.

As a last step, we have to register our filter within our ModelAdmin:

class CountryModelAdmin(admin.ModelAdmin):
    list_display = ('name', 'capital', 'population', 'area', )
    list_filter = (PopulationListFilter, 'currency_name', )

The result should look like this:

Django admin application with custom list filter

Of course, multiple quick filter can be applied at the same time.