# utils/admin.py from operator import itemgetter from django.contrib.admin.filterspecs import FilterSpec, ChoicesFilterSpec from django.db.models import Count from django.utils.encoding import smart_unicode from django.utils.translation import ugettext_lazy as _ def add_compact_filter(field_name, field_lookup='%s__exact', field_detector=lambda f: getattr(f, 'compact_filter', False), field_value_getter=lambda value: value, filtered_value_getter=None, filtered_display_getter=None,): # the simplest case, but they rely on field_name so can't provide as default args if filtered_value_getter is None: filtered_value_getter = lambda obj: getattr(obj, field_name) if filtered_display_getter is None: filtered_display_getter = lambda obj, count: '%s (%s)' % (getattr(obj, field_name), count) class CustomChoiceFilterSpec(ChoicesFilterSpec): def __init__(self, f, request, params, model, model_admin, field_path=None): super(CustomChoiceFilterSpec, self).__init__(f, request, params, model, model_admin, field_path) self.lookup_kwarg = field_lookup % f.name self.lookup_val = request.GET.get(self.lookup_kwarg, None) self.objects = model.objects.all() def choices(self, cl): yield {'selected': self.lookup_val is None, 'query_string': cl.get_query_string({}, [self.lookup_kwarg]), 'display': _('All')} # get the compact list of filter values filtered_values = [] counts = {} for obj in self.objects: value = filtered_value_getter(obj) if value in counts: counts[value] += 1 else: counts[value] = 1 filtered_values.append((obj, filtered_value_getter(obj))) filtered = [(value, filtered_display_getter(obj, counts[value])) for obj, value in filtered_values] for value, display in filtered: yield {'selected': smart_unicode(value) == self.lookup_val, 'query_string': cl.get_query_string({self.lookup_kwarg: field_value_getter(value)}), 'display': display} FilterSpec.filter_specs.insert(0, (field_detector, CustomChoiceFilterSpec)) # EXAMPLE usage: # myapp/admin.py from utils.admin import add_compact_filter from .fields import CountryField add_compact_filter( field_name='country', field_lookup='%s__exact', field_detector=lambda f: isinstance(f, CountryField), filtered_display_getter=lambda obj, count: '%s (%s)' % (obj.get_country_display(), count), )