2 FilterSpec encapsulates the logic for displaying filters in the Django admin.
3 Filters are specified in models with the "list_filter" option.
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.
9 from django
.db
import models
12 class FilterSpec(object):
14 def __init__(self
, f
, request
, params
, model
):
18 def register(cls
, test
, factory
):
19 cls
.filter_specs
.append((test
, factory
))
20 register
= classmethod(register
)
22 def create(cls
, f
, request
, params
, model
):
23 for test
, factory
in cls
.filter_specs
:
25 return factory(f
, request
, params
, model
)
26 create
= classmethod(create
)
31 def choices(self
, cl
):
32 raise NotImplementedError()
35 return self
.field
.verbose_name
40 t
.append(_('<h3>By %s:</h3>\n<ul>\n') % self
.title())
42 for choice
in self
.choices(cl
):
43 t
.append('<li%s><a href="%s">%s</a></li>\n' % \
44 ((choice
['selected'] and ' class="selected"' or ''),
45 choice
['query_string'] ,
50 class RelatedFilterSpec(FilterSpec
):
51 def __init__(self
, f
, request
, params
, model
):
52 super(RelatedFilterSpec
, self
).__init
__(f
, request
, params
, model
)
53 if isinstance(f
, models
.ManyToManyField
):
54 self
.lookup_title
= f
.rel
.to
._meta
.verbose_name
56 self
.lookup_title
= f
.verbose_name
57 self
.lookup_kwarg
= '%s__%s__exact' % (f
.name
, f
.rel
.to
._meta
.pk
.name
)
58 self
.lookup_val
= request
.GET
.get(self
.lookup_kwarg
, None)
59 self
.lookup_choices
= f
.rel
.to
._default
_manager
.all()
62 return len(self
.lookup_choices
) > 1
65 return self
.lookup_title
67 def choices(self
, cl
):
68 yield {'selected': self
.lookup_val
is None,
69 'query_string': cl
.get_query_string({}, [self
.lookup_kwarg
]),
71 for val
in self
.lookup_choices
:
72 pk_val
= getattr(val
, self
.field
.rel
.to
._meta
.pk
.attname
)
73 yield {'selected': self
.lookup_val
== str(pk_val
),
74 'query_string': cl
.get_query_string({self
.lookup_kwarg
: pk_val
}),
77 FilterSpec
.register(lambda f
: bool(f
.rel
), RelatedFilterSpec
)
79 class ChoicesFilterSpec(FilterSpec
):
80 def __init__(self
, f
, request
, params
, model
):
81 super(ChoicesFilterSpec
, self
).__init
__(f
, request
, params
, model
)
82 self
.lookup_kwarg
= '%s__exact' % f
.name
83 self
.lookup_val
= request
.GET
.get(self
.lookup_kwarg
, None)
85 def choices(self
, cl
):
86 yield {'selected': self
.lookup_val
is None,
87 'query_string': cl
.get_query_string({}, [self
.lookup_kwarg
]),
89 for k
, v
in self
.field
.choices
:
90 yield {'selected': str(k
) == self
.lookup_val
,
91 'query_string': cl
.get_query_string({self
.lookup_kwarg
: k
}),
94 FilterSpec
.register(lambda f
: bool(f
.choices
), ChoicesFilterSpec
)
96 class DateFieldFilterSpec(FilterSpec
):
97 def __init__(self
, f
, request
, params
, model
):
98 super(DateFieldFilterSpec
, self
).__init
__(f
, request
, params
, model
)
100 self
.field_generic
= '%s__' % self
.field
.name
102 self
.date_params
= dict([(k
, v
) for k
, v
in params
.items() if k
.startswith(self
.field_generic
)])
104 today
= datetime
.date
.today()
105 one_week_ago
= today
- datetime
.timedelta(days
=7)
106 today_str
= isinstance(self
.field
, models
.DateTimeField
) and today
.strftime('%Y-%m-%d 23:59:59') or today
.strftime('%Y-%m-%d')
110 (_('Today'), {'%s__year' % self
.field
.name
: str(today
.year
),
111 '%s__month' % self
.field
.name
: str(today
.month
),
112 '%s__day' % self
.field
.name
: str(today
.day
)}),
113 (_('Past 7 days'), {'%s__gte' % self
.field
.name
: one_week_ago
.strftime('%Y-%m-%d'),
114 '%s__lte' % f
.name
: today_str
}),
115 (_('This month'), {'%s__year' % self
.field
.name
: str(today
.year
),
116 '%s__month' % f
.name
: str(today
.month
)}),
117 (_('This year'), {'%s__year' % self
.field
.name
: str(today
.year
)})
121 return self
.field
.verbose_name
123 def choices(self
, cl
):
124 for title
, param_dict
in self
.links
:
125 yield {'selected': self
.date_params
== param_dict
,
126 'query_string': cl
.get_query_string(param_dict
, [self
.field_generic
]),
129 FilterSpec
.register(lambda f
: isinstance(f
, models
.DateField
), DateFieldFilterSpec
)
131 class BooleanFieldFilterSpec(FilterSpec
):
132 def __init__(self
, f
, request
, params
, model
):
133 super(BooleanFieldFilterSpec
, self
).__init
__(f
, request
, params
, model
)
134 self
.lookup_kwarg
= '%s__exact' % f
.name
135 self
.lookup_kwarg2
= '%s__isnull' % f
.name
136 self
.lookup_val
= request
.GET
.get(self
.lookup_kwarg
, None)
137 self
.lookup_val2
= request
.GET
.get(self
.lookup_kwarg2
, None)
140 return self
.field
.verbose_name
142 def choices(self
, cl
):
143 for k
, v
in ((_('All'), None), (_('Yes'), '1'), (_('No'), '0')):
144 yield {'selected': self
.lookup_val
== v
and not self
.lookup_val2
,
145 'query_string': cl
.get_query_string({self
.lookup_kwarg
: v
}, [self
.lookup_kwarg2
]),
147 if isinstance(self
.field
, models
.NullBooleanField
):
148 yield {'selected': self
.lookup_val2
== 'True',
149 'query_string': cl
.get_query_string({self
.lookup_kwarg2
: 'True'}, [self
.lookup_kwarg
]),
150 'display': _('Unknown')}
152 FilterSpec
.register(lambda f
: isinstance(f
, models
.BooleanField
) or isinstance(f
, models
.NullBooleanField
), BooleanFieldFilterSpec
)
154 # This should be registered last, because it's a last resort. For example,
155 # if a field is eligible to use the BooleanFieldFilterSpec, that'd be much
156 # more appropriate, and the AllValuesFilterSpec won't get used for it.
157 class AllValuesFilterSpec(FilterSpec
):
158 def __init__(self
, f
, request
, params
, model
):
159 super(AllValuesFilterSpec
, self
).__init
__(f
, request
, params
, model
)
160 self
.lookup_val
= request
.GET
.get(f
.name
, None)
161 self
.lookup_choices
= model
._meta
.admin
.manager
.distinct().order_by(f
.name
).values(f
.name
)
164 return self
.field
.verbose_name
166 def choices(self
, cl
):
167 yield {'selected': self
.lookup_val
is None,
168 'query_string': cl
.get_query_string({}, [self
.field
.name
]),
170 for val
in self
.lookup_choices
:
171 val
= str(val
[self
.field
.name
])
172 yield {'selected': self
.lookup_val
== val
,
173 'query_string': cl
.get_query_string({self
.field
.name
: val
}),
175 FilterSpec
.register(lambda f
: True, AllValuesFilterSpec
)