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
For this example we'll use the
class PopulationListFilter(admin.SimpleListFilter): title = 'population' parameter_name = 'population'
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
yields items for each power of 10 up to the greatest value of the the
population column in the table.
queryset() function applies a
lte filter onto the field to narrow the result.
As a last step, we have to register our filter within our
class CountryModelAdmin(admin.ModelAdmin): list_display = ('name', 'capital', 'population', 'area', ) list_filter = (PopulationListFilter, 'currency_name', )
The result should look like this:
Of course, multiple quick filter can be applied at the same time.