Django

Code

Ticket #5833: custom_filterspecs_plus_fieldless.patch

File custom_filterspecs_plus_fieldless.patch, 10.1 kB (added by korpios, 2 years ago)

Custom FilterSpecs?, also allowing fieldless FilterSpecs?

  • a/django/contrib/admin/filterspecs.py

    old new  
    1414import datetime 
    1515 
    1616class 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): 
    2018        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) 
    3119 
    3220    def has_output(self): 
    3321        return True 
     
    3624        raise NotImplementedError() 
    3725 
    3826    def title(self): 
    39         return self.field.verbose_name 
     27        raise NotImplementedError() 
    4028 
    4129    def output(self, cl): 
    4230        t = [] 
     
    5139            t.append('</ul>\n\n') 
    5240        return mark_safe("".join(t)) 
    5341 
    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 
     42class FieldFilterSpec(FilterSpec): 
     43    field_filter_specs = [] 
     44    def __init__(self, request, params, model, model_admin, field): 
     45        super(FilterSpec, self).__init__(request, params, model, model_admin) 
     46        self.field = field 
     47 
     48    def register(cls, test, factory): 
     49        cls.field_filter_specs.append((test, factory)) 
     50    register = classmethod(register) 
     51 
     52    def create(cls, request, params, model, model_admin, field): 
     53        for test, factory in cls.field_filter_specs: 
     54            if test(field): 
     55                return factory(request, params, model, model_admin, field) 
     56    create = classmethod(create) 
     57 
     58    def title(self): 
     59        return self.field.verbose_name 
     60 
     61class RelatedFilterSpec(FieldFilterSpec): 
     62    def __init__(self, request, params, model, model_admin, field): 
     63        super(RelatedFilterSpec, self).__init__(request, params, model, model_admin, field) 
     64        if isinstance(field, models.ManyToManyField): 
     65            self.lookup_title = field.rel.to._meta.verbose_name 
    5966        else: 
    60             self.lookup_title = f.verbose_name 
    61         self.lookup_kwarg = '%s__%s__exact' % (f.name, f.rel.to._meta.pk.name) 
     67            self.lookup_title = field.verbose_name 
     68        self.lookup_kwarg = '%s__%s__exact' % (field.name, field.rel.to._meta.pk.name) 
    6269        self.lookup_val = request.GET.get(self.lookup_kwarg, None) 
    63         self.lookup_choices = f.rel.to._default_manager.all() 
     70        self.lookup_choices = field.rel.to._default_manager.all() 
    6471 
    6572    def has_output(self): 
    6673        return len(self.lookup_choices) > 1 
     
    7784            yield {'selected': self.lookup_val == smart_unicode(pk_val), 
    7885                   'query_string': cl.get_query_string({self.lookup_kwarg: pk_val}), 
    7986                   'display': val} 
     87FieldFilterSpec.register(lambda f: bool(f.rel), RelatedFilterSpec) 
    8088 
    81 FilterSpec.register(lambda f: bool(f.rel), RelatedFilterSpec) 
    82  
    83 class ChoicesFilterSpec(FilterSpec): 
    84     def __init__(self, f, request, params, model, model_admin): 
    85         super(ChoicesFilterSpec, self).__init__(f, request, params, model, model_admin) 
    86         self.lookup_kwarg = '%s__exact' % f.name 
     89class ChoicesFilterSpec(FieldFilterSpec): 
     90    def __init__(self, request, params, model, model_admin, field): 
     91        super(ChoicesFilterSpec, self).__init__(request, params, model, model_admin, field) 
     92        self.lookup_kwarg = '%s__exact' % field.name 
    8793        self.lookup_val = request.GET.get(self.lookup_kwarg, None) 
    8894 
    8995    def choices(self, cl): 
     
    94100            yield {'selected': smart_unicode(k) == self.lookup_val, 
    95101                    'query_string': cl.get_query_string({self.lookup_kwarg: k}), 
    96102                    'display': v} 
     103FieldFilterSpec.register(lambda f: bool(f.choices), ChoicesFilterSpec) 
    97104 
    98 FilterSpec.register(lambda f: bool(f.choices), ChoicesFilterSpec) 
    99  
    100 class DateFieldFilterSpec(FilterSpec): 
    101     def __init__(self, f, request, params, model, model_admin): 
    102         super(DateFieldFilterSpec, self).__init__(f, request, params, model, model_admin) 
     105class DateFieldFilterSpec(FieldFilterSpec): 
     106    def __init__(self, request, params, model, model_admin, field): 
     107        super(DateFieldFilterSpec, self).__init__(request, params, model, model_admin, field) 
    103108 
    104109        self.field_generic = '%s__' % self.field.name 
    105110 
     
    115120                       '%s__month' % self.field.name: str(today.month), 
    116121                       '%s__day' % self.field.name: str(today.day)}), 
    117122            (_('Past 7 days'), {'%s__gte' % self.field.name: one_week_ago.strftime('%Y-%m-%d'), 
    118                              '%s__lte' % f.name: today_str}), 
     123                             '%s__lte' % field.name: today_str}), 
    119124            (_('This month'), {'%s__year' % self.field.name: str(today.year), 
    120                              '%s__month' % f.name: str(today.month)}), 
     125                             '%s__month' % field.name: str(today.month)}), 
    121126            (_('This year'), {'%s__year' % self.field.name: str(today.year)}) 
    122127        ) 
    123128 
     
    129134            yield {'selected': self.date_params == param_dict, 
    130135                   'query_string': cl.get_query_string(param_dict, [self.field_generic]), 
    131136                   'display': title} 
     137FieldFilterSpec.register(lambda f: isinstance(f, models.DateField), DateFieldFilterSpec) 
    132138 
    133 FilterSpec.register(lambda f: isinstance(f, models.DateField), DateFieldFilterSpec) 
    134  
    135 class BooleanFieldFilterSpec(FilterSpec): 
    136     def __init__(self, f, request, params, model, model_admin): 
    137         super(BooleanFieldFilterSpec, self).__init__(f, request, params, model, model_admin) 
    138         self.lookup_kwarg = '%s__exact' % f.name 
    139         self.lookup_kwarg2 = '%s__isnull' % f.name 
     139class BooleanFieldFilterSpec(FieldFilterSpec): 
     140    def __init__(self, request, params, model, model_admin, field): 
     141        super(BooleanFieldFilterSpec, self).__init__(request, params, model, model_admin, field) 
     142        self.lookup_kwarg = '%s__exact' % field.name 
     143        self.lookup_kwarg2 = '%s__isnull' % field.name 
    140144        self.lookup_val = request.GET.get(self.lookup_kwarg, None) 
    141145        self.lookup_val2 = request.GET.get(self.lookup_kwarg2, None) 
    142146 
     
    152156            yield {'selected': self.lookup_val2 == 'True', 
    153157                   'query_string': cl.get_query_string({self.lookup_kwarg2: 'True'}, [self.lookup_kwarg]), 
    154158                   'display': _('Unknown')} 
    155  
    156 FilterSpec.register(lambda f: isinstance(f, models.BooleanField) or isinstance(f, models.NullBooleanField), BooleanFieldFilterSpec) 
     159FieldFilterSpec.register(lambda f: isinstance(f, models.BooleanField) or isinstance(f, models.NullBooleanField), BooleanFieldFilterSpec) 
    157160 
    158161# This should be registered last, because it's a last resort. For example, 
    159162# if a field is eligible to use the BooleanFieldFilterSpec, that'd be much 
    160163# more appropriate, and the AllValuesFilterSpec won't get used for it. 
    161 class AllValuesFilterSpec(FilterSpec): 
    162     def __init__(self, f, request, params, model, model_admin): 
    163         super(AllValuesFilterSpec, self).__init__(f, request, params, model, model_admin
    164         self.lookup_val = request.GET.get(f.name, None) 
    165         self.lookup_choices = model_admin.queryset(request).distinct().order_by(f.name).values(f.name) 
     164class AllValuesFilterSpec(FieldFilterSpec): 
     165    def __init__(self, request, params, model, model_admin, field): 
     166        super(AllValuesFilterSpec, self).__init__(request, params, model, model_admin, field
     167        self.lookup_val = request.GET.get(field.name, None) 
     168        self.lookup_choices = model_admin.queryset(request).distinct().order_by(field.name).values(field.name) 
    166169 
    167170    def title(self): 
    168171        return self.field.verbose_name 
     
    176179            yield {'selected': self.lookup_val == val, 
    177180                   'query_string': cl.get_query_string({self.field.name: val}), 
    178181                   'display': val} 
    179 FilterSpec.register(lambda f: True, AllValuesFilterSpec) 
     182FieldFilterSpec.register(lambda f: True, AllValuesFilterSpec) 
     183 
  • a/django/contrib/admin/views/main.py

    old new  
    11from django import template 
    2 from django.contrib.admin.filterspecs import FilterSpec 
     2from django.contrib.admin.filterspecs import FieldFilterSpec 
    33from django.contrib.admin.options import IncorrectLookupParameters 
    44from django.contrib.admin.views.decorators import staff_member_required 
    55from django.core.exceptions import ObjectDoesNotExist 
     
    158158    def get_filters(self, request): 
    159159        filter_specs = [] 
    160160        if self.list_filter and not self.opts.one_to_one_field: 
    161             filter_fields = [self.lookup_opts.get_field(field_name) for field_name in self.list_filter] 
    162             for f in filter_fields: 
    163                 spec = FilterSpec.create(f, request, self.params, self.model, self.model_admin) 
     161            for item in self.list_filter: 
     162                if callable(item): 
     163                    spec = item( 
     164                        request, self.params, self.model, self.model_admin 
     165                    ) 
     166                else: 
     167                    if type(item) is tuple: 
     168                        (name, factory) = item 
     169                    else: 
     170                        name = item 
     171                        factory = FieldFilterSpec.create 
     172                    f = self.lookup_opts.get_field(name) 
     173                    spec = factory( 
     174                        request, self.params, self.model, self.model_admin, f 
     175                    ) 
    164176                if spec and spec.has_output(): 
    165177                    filter_specs.append(spec) 
    166178        return filter_specs, bool(filter_specs)