Django

Code

root/django/trunk/django/contrib/admin/filterspecs.py

Revision 9241, 7.5 kB (checked in by kmtracey, 1 month ago)

Fixed #3096 -- Make admin list_filters respect limit_choices_to.

  • Property svn:eol-style set to native
Line 
1 """
2 FilterSpec encapsulates the logic for displaying filters in the Django admin.
3 Filters are specified in models with the "list_filter" option.
4
5 Each filter subclass knows how to display a filter for a field that passes a
6 certain test -- e.g. being a DateField or ForeignKey.
7 """
8
9 from django.db import models
10 from django.utils.encoding import smart_unicode, iri_to_uri
11 from django.utils.translation import ugettext as _
12 from django.utils.html import escape
13 from django.utils.safestring import mark_safe
14 import datetime
15
16 class FilterSpec(object):
17     filter_specs = []
18     def __init__(self, f, request, params, model, model_admin):
19         self.field = f
20         self.params = params
21
22     def register(cls, test, factory):
23         cls.filter_specs.append((test, factory))
24     register = classmethod(register)
25
26     def create(cls, f, request, params, model, model_admin):
27         for test, factory in cls.filter_specs:
28             if test(f):
29                 return factory(f, request, params, model, model_admin)
30     create = classmethod(create)
31
32     def has_output(self):
33         return True
34
35     def choices(self, cl):
36         raise NotImplementedError()
37
38     def title(self):
39         return self.field.verbose_name
40
41     def output(self, cl):
42         t = []
43         if self.has_output():
44             t.append(_(u'<h3>By %s:</h3>\n<ul>\n') % escape(self.title()))
45
46             for choice in self.choices(cl):
47                 t.append(u'<li%s><a href="%s">%s</a></li>\n' % \
48                     ((choice['selected'] and ' class="selected"' or ''),
49                      iri_to_uri(choice['query_string']),
50                      choice['display']))
51             t.append('</ul>\n\n')
52         return mark_safe("".join(t))
53
54 class RelatedFilterSpec(FilterSpec):
55     def __init__(self, f, request, params, model, model_admin):
56         super(RelatedFilterSpec, self).__init__(f, request, params, model, model_admin)
57         if isinstance(f, models.ManyToManyField):
58             self.lookup_title = f.rel.to._meta.verbose_name
59         else:
60             self.lookup_title = f.verbose_name
61         self.lookup_kwarg = '%s__%s__exact' % (f.name, f.rel.to._meta.pk.name)
62         self.lookup_val = request.GET.get(self.lookup_kwarg, None)
63         self.lookup_choices = f.get_choices(include_blank=False)
64
65     def has_output(self):
66         return len(self.lookup_choices) > 1
67
68     def title(self):
69         return self.lookup_title
70
71     def choices(self, cl):
72         yield {'selected': self.lookup_val is None,
73                'query_string': cl.get_query_string({}, [self.lookup_kwarg]),
74                'display': _('All')}
75         for pk_val, val in self.lookup_choices:
76             yield {'selected': self.lookup_val == smart_unicode(pk_val),
77                    'query_string': cl.get_query_string({self.lookup_kwarg: pk_val}),
78                    'display': val}
79
80 FilterSpec.register(lambda f: bool(f.rel), RelatedFilterSpec)
81
82 class ChoicesFilterSpec(FilterSpec):
83     def __init__(self, f, request, params, model, model_admin):
84         super(ChoicesFilterSpec, self).__init__(f, request, params, model, model_admin)
85         self.lookup_kwarg = '%s__exact' % f.name
86         self.lookup_val = request.GET.get(self.lookup_kwarg, None)
87
88     def choices(self, cl):
89         yield {'selected': self.lookup_val is None,
90                'query_string': cl.get_query_string({}, [self.lookup_kwarg]),
91                'display': _('All')}
92         for k, v in self.field.choices:
93             yield {'selected': smart_unicode(k) == self.lookup_val,
94                     'query_string': cl.get_query_string({self.lookup_kwarg: k}),
95                     'display': v}
96
97 FilterSpec.register(lambda f: bool(f.choices), ChoicesFilterSpec)
98
99 class DateFieldFilterSpec(FilterSpec):
100     def __init__(self, f, request, params, model, model_admin):
101         super(DateFieldFilterSpec, self).__init__(f, request, params, model, model_admin)
102
103         self.field_generic = '%s__' % self.field.name
104
105         self.date_params = dict([(k, v) for k, v in params.items() if k.startswith(self.field_generic)])
106
107         today = datetime.date.today()
108         one_week_ago = today - datetime.timedelta(days=7)
109         today_str = isinstance(self.field, models.DateTimeField) and today.strftime('%Y-%m-%d 23:59:59') or today.strftime('%Y-%m-%d')
110
111         self.links = (
112             (_('Any date'), {}),
113             (_('Today'), {'%s__year' % self.field.name: str(today.year),
114                        '%s__month' % self.field.name: str(today.month),
115                        '%s__day' % self.field.name: str(today.day)}),
116             (_('Past 7 days'), {'%s__gte' % self.field.name: one_week_ago.strftime('%Y-%m-%d'),
117                              '%s__lte' % f.name: today_str}),
118             (_('This month'), {'%s__year' % self.field.name: str(today.year),
119                              '%s__month' % f.name: str(today.month)}),
120             (_('This year'), {'%s__year' % self.field.name: str(today.year)})
121         )
122
123     def title(self):
124         return self.field.verbose_name
125
126     def choices(self, cl):
127         for title, param_dict in self.links:
128             yield {'selected': self.date_params == param_dict,
129                    'query_string': cl.get_query_string(param_dict, [self.field_generic]),
130                    'display': title}
131
132 FilterSpec.register(lambda f: isinstance(f, models.DateField), DateFieldFilterSpec)
133
134 class BooleanFieldFilterSpec(FilterSpec):
135     def __init__(self, f, request, params, model, model_admin):
136         super(BooleanFieldFilterSpec, self).__init__(f, request, params, model, model_admin)
137         self.lookup_kwarg = '%s__exact' % f.name
138         self.lookup_kwarg2 = '%s__isnull' % f.name
139         self.lookup_val = request.GET.get(self.lookup_kwarg, None)
140         self.lookup_val2 = request.GET.get(self.lookup_kwarg2, None)
141
142     def title(self):
143         return self.field.verbose_name
144
145     def choices(self, cl):
146         for k, v in ((_('All'), None), (_('Yes'), '1'), (_('No'), '0')):
147             yield {'selected': self.lookup_val == v and not self.lookup_val2,
148                    'query_string': cl.get_query_string({self.lookup_kwarg: v}, [self.lookup_kwarg2]),
149                    'display': k}
150         if isinstance(self.field, models.NullBooleanField):
151             yield {'selected': self.lookup_val2 == 'True',
152                    'query_string': cl.get_query_string({self.lookup_kwarg2: 'True'}, [self.lookup_kwarg]),
153                    'display': _('Unknown')}
154
155 FilterSpec.register(lambda f: isinstance(f, models.BooleanField) or isinstance(f, models.NullBooleanField), BooleanFieldFilterSpec)
156
157 # This should be registered last, because it's a last resort. For example,
158 # if a field is eligible to use the BooleanFieldFilterSpec, that'd be much
159 # more appropriate, and the AllValuesFilterSpec won't get used for it.
160 class AllValuesFilterSpec(FilterSpec):
161     def __init__(self, f, request, params, model, model_admin):
162         super(AllValuesFilterSpec, self).__init__(f, request, params, model, model_admin)
163         self.lookup_val = request.GET.get(f.name, None)
164         self.lookup_choices = model_admin.queryset(request).distinct().order_by(f.name).values(f.name)
165
166     def title(self):
167         return self.field.verbose_name
168
169     def choices(self, cl):
170         yield {'selected': self.lookup_val is None,
171                'query_string': cl.get_query_string({}, [self.field.name]),
172                'display': _('All')}
173         for val in self.lookup_choices:
174             val = smart_unicode(val[self.field.name])
175             yield {'selected': self.lookup_val == val,
176                    'query_string': cl.get_query_string({self.field.name: val}),
177                    'display': val}
178 FilterSpec.register(lambda f: True, AllValuesFilterSpec)
Note: See TracBrowser for help on using the browser.