Limit ForeignKey filter values to those that have a relationship with current model

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
from django.contrib.admin.filterspecs import FilterSpec
from django.contrib.admin.filterspecs import RelatedFilterSpec


class FKFilterSpec(RelatedFilterSpec):
    def __init__(self, f, request, params, model, model_admin):
        filter_by_key = f.name+'_fk_filter_by'
        filter_by_val = getattr(model_admin, filter_by_key, None)
        if filter_by_val != None:
            self.fk_filter_on = True
            # we call FilterSpec constructor, not RelatedFilterSpec
            # constructor; RelatedFilterSpec constructor will try to          
            # get all the pk values on the related models, which we
            # won't need.
            FilterSpec.__init__(self, f, request, params, model, model_admin)
            filter_name_key = f.name+'_fk_filter_name'
            filter_name_val = getattr(model_admin, filter_name_key, None)
            if filter_name_val == None:
                self.lookup_title = f.verbose_name
            else:
                self.lookup_title = f.verbose_name+' '+filter_name_val
            self.lookup_kwarg = f.name+'__'+filter_by_val+'__exact'
            self.lookup_val = request.GET.get(self.lookup_kwarg, None)
            values_list = f.rel.to.objects.values_list(filter_by_val, flat=True).distinct()
            self.lookup_choices = list(values_list)
        else:
            RelatedFilterSpec.__init__(self, f, request, params, model, model_admin)
            self.fk_filter_on = False
            filter_related_key = f.name+'_fk_filter_related_only'
            filter_related_val = getattr(model_admin, filter_related_key, False)
            filter_nf_key = f.name+'_fk_filter_name_field'
            filter_nf_val = getattr(model_admin, filter_nf_key, 'pk')
            if filter_related_val:
                values_list = model_admin.queryset(request).distinct().values_list(f.name+'__pk',f.name+'__'+filter_nf_val).order_by(f.name+'__'+filter_nf_val).distinct()
                self.lookup_choices = list(values_list)


    def choices(self, cl):
        yield {'selected': self.lookup_val is None,
               'query_string': cl.get_query_string({}, [self.lookup_kwarg]),
               'display': _('All')}
        if self.fk_filter_on:
            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': val}
        else:
            for pk_val,val in self.lookup_choices:
                yield {'selected': self.lookup_val == smart_unicode(pk_val),
                       'query_string': cl.get_query_string({self.lookup_kwarg: pk_val}),
                       'display': val}

FilterSpec.filter_specs.insert(0, (lambda f: bool(f.rel), FKFilterSpec))

More like this

  1. FilterSpec for ForeignKeys to auth.User with HTML input tag for ModelAdmin.list_filter by loic 2 years, 11 months ago
  2. Changelist filter by ForeignKey by overclocked 3 years, 5 months ago
  3. ForeignKey filterspec by luc_j 3 years, 7 months ago
  4. Custom change_list filter based on SimpleListFilter shows only referenced (related, used) values by darklow 1 year, 2 months ago
  5. Fix for the bad behaviour of GenericForeignKey field by pinkeen 3 years, 3 months ago

Comments

overclocked (on November 12, 2010):

I merged the changes described here into 2260.

#

(Forgotten your password?)