Ticket #5833: 5833-custom-filter-spec-1.2.1.diff
File 5833-custom-filter-spec-1.2.1.diff, 14.7 KB (added by , 14 years ago) |
---|
-
django/contrib/admin/validation.py
2 2 from django.db import models 3 3 from django.forms.models import (BaseModelForm, BaseModelFormSet, fields_for_model, 4 4 _get_foreign_key) 5 from django.contrib.admin.filterspecs import FilterSpec, FieldFilterSpec 5 6 from django.contrib.admin.options import flatten_fieldsets, BaseModelAdmin 6 7 from django.contrib.admin.options import HORIZONTAL, VERTICAL 7 8 from django.contrib.admin.util import lookup_field … … 48 49 fetch_attr(cls, model, opts, 'list_display_links[%d]' % idx, field) 49 50 if field not in cls.list_display: 50 51 raise ImproperlyConfigured("'%s.list_display_links[%d]'" 51 " refers to '%s' which is not defined in 'list_display'."52 " refers to '%s' which is not defined in 'list_display'." 52 53 % (cls.__name__, idx, field)) 53 54 54 55 # list_filter 55 56 if hasattr(cls, 'list_filter'): 56 57 check_isseq(cls, 'list_filter', cls.list_filter) 57 for idx, field in enumerate(cls.list_filter): 58 get_field(cls, model, opts, 'list_filter[%d]' % idx, field) 58 for idx, item in enumerate(cls.list_filter): 59 if callable(item) and not isinstance(item, models.Field): 60 # Make sure the item is not FieldFilterSpec or a subclass thereof 61 # since it has a different __init__ signature, which leads to 62 # strange exceptions if not caught here 63 if not issubclass(item, FilterSpec) or issubclass(item, FieldFilterSpec): 64 raise ImproperlyConfigured("'%s.list_filter[%d]' is '%s' which is not of type FilterSpec." 65 % (cls.__name__, idx, item)) 66 else: 67 field = None 68 try: 69 field, factory = item 70 except (TypeError, ValueError): 71 pass 72 else: 73 if not issubclass(factory, FieldFilterSpec): 74 raise ImproperlyConfigured("'%s.list_filter[%d][1]'" 75 " refers to '%s' which is not of type FieldFilterSpec." 76 % (cls.__name__, idx, factory.__name__)) 77 # Validate field 78 if not isinstance(field, models.Field): 79 get_field(cls, model, opts, 'list_filter[%d]' % idx, field) 80 elif not isinstance(item, models.Field): 81 get_field(cls, model, opts, 'list_filter[%d]' % idx, item) 59 82 60 83 # list_per_page = 100 61 84 if hasattr(cls, 'list_per_page') and not isinstance(cls.list_per_page, int): -
django/contrib/admin/__init__.py
1 from django.contrib.admin.filterspecs import FilterSpec, FieldFilterSpec 1 2 from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME 2 3 from django.contrib.admin.options import ModelAdmin, HORIZONTAL, VERTICAL 3 4 from django.contrib.admin.options import StackedInline, TabularInline -
django/contrib/admin/filterspecs.py
14 14 import datetime 15 15 16 16 class FilterSpec(object): 17 filter_specs = [] 18 def __init__(self, f, request, params, model, model_admin): 19 self.field = f 17 def __init__(self, request, params, model, model_admin): 20 18 self.params = params 21 19 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 20 def has_output(self): 33 21 return True 34 22 35 23 def choices(self, cl): 36 24 raise NotImplementedError() 37 25 38 26 def title(self): 39 return self.field.verbose_name 27 raise NotImplementedError() 28 29 def get_query_set(self, cl, qs): 30 return False 31 32 def consumed_params(self): 33 """ 34 Return a list of parameters to consume from the change list querystring. 35 36 Override this for non-field based FilterSpecs subclasses in order 37 to consume custom GET parameters, as any GET parameters that are not 38 consumed and are not a field name raises an exception. 39 """ 40 return [] 40 41 41 42 def output(self, cl): 42 43 t = [] … … 50 51 choice['display'])) 51 52 t.append('</ul>\n\n') 52 53 return mark_safe("".join(t)) 54 55 class FieldFilterSpec(FilterSpec): 56 field_filter_specs = [] 57 _high_priority_index = 0 58 59 def __init__(self, request, params, model, model_admin, f): 60 super(FieldFilterSpec, self).__init__(request, params, model, model_admin) 61 self.field = f 62 63 def title(self): 64 return self.field.verbose_name 65 66 def register(cls, test, factory, high_priority=True): 67 if high_priority: 68 cls.field_filter_specs.insert(cls._high_priority_index, (test, factory)) 69 cls._high_priority_index += 1 70 else: 71 cls.field_filter_specs.append((test, factory)) 72 register = classmethod(register) 73 74 def create(cls, request, params, model, model_admin, f): 75 for test, factory in cls.field_filter_specs: 76 if test(f): 77 return factory(request, params, model, model_admin, f) 78 create = classmethod(create) 53 79 54 class RelatedFilterSpec(Fi lterSpec):55 def __init__(self, f, request, params, model, model_admin):56 super(RelatedFilterSpec, self).__init__( f, request, params, model, model_admin)80 class RelatedFilterSpec(FieldFilterSpec): 81 def __init__(self, request, params, model, model_admin, f): 82 super(RelatedFilterSpec, self).__init__(request, params, model, model_admin, f) 57 83 if isinstance(f, models.ManyToManyField): 58 84 self.lookup_title = f.rel.to._meta.verbose_name 59 85 else: … … 78 104 'query_string': cl.get_query_string({self.lookup_kwarg: pk_val}), 79 105 'display': val} 80 106 81 Fi lterSpec.register(lambda f: bool(f.rel), RelatedFilterSpec)107 FieldFilterSpec.register(lambda f: bool(f.rel), RelatedFilterSpec, False) 82 108 83 class ChoicesFilterSpec(Fi lterSpec):84 def __init__(self, f, request, params, model, model_admin):85 super(ChoicesFilterSpec, self).__init__( f, request, params, model, model_admin)109 class ChoicesFilterSpec(FieldFilterSpec): 110 def __init__(self, request, params, model, model_admin, f): 111 super(ChoicesFilterSpec, self).__init__(request, params, model, model_admin, f) 86 112 self.lookup_kwarg = '%s__exact' % f.name 87 113 self.lookup_val = request.GET.get(self.lookup_kwarg, None) 88 114 … … 95 121 'query_string': cl.get_query_string({self.lookup_kwarg: k}), 96 122 'display': v} 97 123 98 Fi lterSpec.register(lambda f: bool(f.choices), ChoicesFilterSpec)124 FieldFilterSpec.register(lambda f: bool(f.choices), ChoicesFilterSpec, False) 99 125 100 class DateFieldFilterSpec(Fi lterSpec):101 def __init__(self, f, request, params, model, model_admin):102 super(DateFieldFilterSpec, self).__init__( f, request, params, model, model_admin)126 class DateFieldFilterSpec(FieldFilterSpec): 127 def __init__(self, request, params, model, model_admin, f): 128 super(DateFieldFilterSpec, self).__init__(request, params, model, model_admin, f) 103 129 104 130 self.field_generic = '%s__' % self.field.name 105 131 … … 121 147 (_('This year'), {'%s__year' % self.field.name: str(today.year)}) 122 148 ) 123 149 124 def title(self):125 return self.field.verbose_name126 127 150 def choices(self, cl): 128 151 for title, param_dict in self.links: 129 152 yield {'selected': self.date_params == param_dict, 130 153 'query_string': cl.get_query_string(param_dict, [self.field_generic]), 131 154 'display': title} 132 155 133 Fi lterSpec.register(lambda f: isinstance(f, models.DateField), DateFieldFilterSpec)156 FieldFilterSpec.register(lambda f: isinstance(f, models.DateField), DateFieldFilterSpec, False) 134 157 135 class BooleanFieldFilterSpec(Fi lterSpec):136 def __init__(self, f, request, params, model, model_admin):137 super(BooleanFieldFilterSpec, self).__init__( f, request, params, model, model_admin)158 class BooleanFieldFilterSpec(FieldFilterSpec): 159 def __init__(self, request, params, model, model_admin, f): 160 super(BooleanFieldFilterSpec, self).__init__(request, params, model, model_admin, f) 138 161 self.lookup_kwarg = '%s__exact' % f.name 139 162 self.lookup_kwarg2 = '%s__isnull' % f.name 140 163 self.lookup_val = request.GET.get(self.lookup_kwarg, None) 141 164 self.lookup_val2 = request.GET.get(self.lookup_kwarg2, None) 142 165 143 def title(self):144 return self.field.verbose_name145 146 166 def choices(self, cl): 147 167 for k, v in ((_('All'), None), (_('Yes'), '1'), (_('No'), '0')): 148 168 yield {'selected': self.lookup_val == v and not self.lookup_val2, … … 153 173 'query_string': cl.get_query_string({self.lookup_kwarg2: 'True'}, [self.lookup_kwarg]), 154 174 'display': _('Unknown')} 155 175 156 Fi lterSpec.register(lambda f: isinstance(f, models.BooleanField) or isinstance(f, models.NullBooleanField), BooleanFieldFilterSpec)176 FieldFilterSpec.register(lambda f: isinstance(f, models.BooleanField) or isinstance(f, models.NullBooleanField), BooleanFieldFilterSpec, False) 157 177 158 178 # This should be registered last, because it's a last resort. For example, 159 179 # if a field is eligible to use the BooleanFieldFilterSpec, that'd be much 160 180 # more appropriate, and the AllValuesFilterSpec won't get used for it. 161 class AllValuesFilterSpec(Fi lterSpec):162 def __init__(self, f, request, params, model, model_admin):163 super(AllValuesFilterSpec, self).__init__( f, request, params, model, model_admin)181 class AllValuesFilterSpec(FieldFilterSpec): 182 def __init__(self, request, params, model, model_admin, f): 183 super(AllValuesFilterSpec, self).__init__(request, params, model, model_admin, f) 164 184 self.lookup_val = request.GET.get(f.name, None) 165 185 self.lookup_choices = model_admin.queryset(request).distinct().order_by(f.name).values(f.name) 166 186 167 def title(self):168 return self.field.verbose_name169 170 187 def choices(self, cl): 171 188 yield {'selected': self.lookup_val is None, 172 189 'query_string': cl.get_query_string({}, [self.field.name]), … … 176 193 yield {'selected': self.lookup_val == val, 177 194 'query_string': cl.get_query_string({self.field.name: val}), 178 195 'display': val} 179 Fi lterSpec.register(lambda f: True, AllValuesFilterSpec)196 FieldFilterSpec.register(lambda f: True, AllValuesFilterSpec, False) -
django/contrib/admin/views/main.py
1 from django.contrib.admin.filterspecs import FilterSpec 1 from django.contrib.admin.filterspecs import FilterSpec, FieldFilterSpec 2 2 from django.contrib.admin.options import IncorrectLookupParameters 3 3 from django.contrib.admin.util import quote 4 4 from django.core.paginator import Paginator, InvalidPage … … 58 58 if ERROR_FLAG in self.params: 59 59 del self.params[ERROR_FLAG] 60 60 61 self.filter_specs, self.has_filters = self.get_filters(request) 61 62 self.order_field, self.order_type = self.get_ordering() 62 63 self.query = request.GET.get(SEARCH_VAR, '') 63 64 self.query_set = self.get_query_set() 64 65 self.get_results(request) 65 66 self.title = (self.is_popup and ugettext('Select %s') % force_unicode(self.opts.verbose_name) or ugettext('Select %s to change') % force_unicode(self.opts.verbose_name)) 66 self.filter_specs, self.has_filters = self.get_filters(request)67 67 self.pk_attname = self.lookup_opts.pk.attname 68 68 69 69 def get_filters(self, request): 70 70 filter_specs = [] 71 71 if self.list_filter: 72 filter_fields = [self.lookup_opts.get_field(field_name) for field_name in self.list_filter] 73 for f in filter_fields: 74 spec = FilterSpec.create(f, request, self.params, self.model, self.model_admin) 72 for item in self.list_filter: 73 if callable(item): 74 spec = item(request, self.params, self.model, self.model_admin) 75 else: 76 try: 77 field, factory = item 78 except (TypeError, ValueError): 79 field, factory = item, FieldFilterSpec.create 80 if not isinstance(field, models.Field): 81 field = self.lookup_opts.get_field(field) 82 spec = factory(request, self.params, self.model, 83 self.model_admin, field) 75 84 if spec and spec.has_output(): 76 85 filter_specs.append(spec) 77 86 return filter_specs, bool(filter_specs) … … 169 178 for i in (ALL_VAR, ORDER_VAR, ORDER_TYPE_VAR, SEARCH_VAR, IS_POPUP_VAR): 170 179 if i in lookup_params: 171 180 del lookup_params[i] 181 key = '' 172 182 for key, value in lookup_params.items(): 173 183 if not isinstance(key, str): 174 184 # 'key' will be used as a keyword argument later, so Python … … 179 189 # if key ends with __in, split parameter into separate values 180 190 if key.endswith('__in'): 181 191 lookup_params[key] = value.split(',') 192 193 # Let every filter spec modify the qs and params to its liking 194 for filter_spec in self.filter_specs: 195 new_qs = filter_spec.get_query_set(self, qs) 196 if new_qs is not None and new_qs is not False: 197 qs = new_qs 198 # Only consume params if we got a new queryset 199 for param in filter_spec.consumed_params(): 200 try: 201 del lookup_params[param] 202 except KeyError: 203 pass 182 204 183 205 # if key ends with __isnull, special case '' and false 184 206 if key.endswith('__isnull'):