#-*- coding: utf-8 -*- #!/usr/bin/env python from django.db import models from django.contrib.admin.filterspecs import FilterSpec, ChoicesFilterSpec from django.utils.encoding import smart_unicode from django.utils.translation import ugettext as _ class FkFilterSpec(ChoicesFilterSpec): def __init__(self, f, request, params, model, model_admin): super(FkFilterSpec, self).__init__(f, request, params, model, model_admin) # ******* Extract parameters ******** the_args = f.fk_filterspec.copy() #The field of the related table fk_field = the_args['fk_field'] #The name in the related table to use as label in the choices label = the_args.pop('label', '') #a title: by default the lookup arg self.filter_title = the_args.pop('title', '') #the foreign key field. By default the field the filter is assigned fk = the_args.pop('fk', f.name) # ******* Build the filter definition ******** self.lookup_kwarg = '{0}__{1}'.format(fk, fk_field) self.lookup_val = request.GET.get(self.lookup_kwarg, None) self.lookup_labels = {} #get the list of values values_list = model.objects.values_list(self.lookup_kwarg, flat=True) #get the if label: label_field = '{0}__{1}__{2}'.format(fk, fk_field, label) else: label_field = '{0}__{1}'.format(fk, fk_field) labels = model.objects.values_list(label_field, flat=True) for (v, l) in zip(values_list, labels): self.lookup_labels[v] = l self.lookup_choices = self.lookup_labels.keys() def choices(self, cl): yield {'selected': self.lookup_val is None, 'query_string': cl.get_query_string({}, [self.lookup_kwarg]), 'display': _('All')} for val in self.lookup_choices: yield {'selected': smart_unicode(val) == self.lookup_val, 'query_string': cl.get_query_string({self.lookup_kwarg: val}), 'display': smart_unicode(self.lookup_labels[val])} def title(self): if self.filter_title: return self.filter_title else: return super(FkFilterSpec, self).title() @classmethod def register_filterspec(cls): """register the filter. To be called in the models.py""" FilterSpec.filter_specs.insert(0, (lambda f: len(getattr(f, 'fk_filterspec', [])), cls) ) ### In the models.py from filterspecs import FkFilterSpec FkFilterSpec.register_filterspec() class Country(models.Model): name = models.CharField(max_length=100) class City(models.Model): name = models.CharField(max_length=100) country = models.ForeignKey(Country) class Company(models.Model): name = models.CharField(max_length=100) city = models.ForeignKey(City) city.fk_filterspec = {'fk_field':'country', 'label':'name', 'title':'Country'}