Django

Code

Ticket #5833: filterspec_with_custom_queryset_against_1_0.diff

File filterspec_with_custom_queryset_against_1_0.diff, 11.3 kB (added by fas, 2 years ago)

Custom Filtersets (fieldless) with custom query set manipulation.

  • django/contrib/admin/validation.py

    old new  
    66from django.core.exceptions import ImproperlyConfigured 
    77from django.db import models 
    88from django.forms.models import BaseModelForm, BaseModelFormSet, fields_for_model 
     9from django.contrib.admin.filterspecs import FieldFilterSpec 
    910from django.contrib.admin.options import flatten_fieldsets, BaseModelAdmin 
    1011from django.contrib.admin.options import HORIZONTAL, VERTICAL 
    1112 
     
    5455    # list_filter 
    5556    if hasattr(cls, 'list_filter'): 
    5657        check_isseq(cls, 'list_filter', cls.list_filter) 
    57         for idx, field in enumerate(cls.list_filter): 
     58        for idx, item in enumerate(cls.list_filter): 
     59            if callable(item): 
     60                #TODO find out whether item  is of type FilterSpec 
     61                pass 
     62            else: 
     63                if type(item) is tuple: 
     64                    (field, factory) = item 
     65                        #TODO find out whether factory is of type (or a descendant of) FieldFilterSpec 
     66                        #raise ImproperlyConfigured("'%s.list_filter[%d][1]' is not of type FieldFilterSpec."   
     67                        #    % (cls.__name__, idx)) 
     68                else: 
     69                    field = item 
     70            # validate field 
    5871            get_field(cls, model, opts, 'list_filter[%d]' % idx, field) 
    5972 
    6073    # list_per_page = 100 
  • 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 
     18    def __init__(self, request, params, model, model_admin): 
    2019        self.params = params 
    2120 
    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  
    3221    def has_output(self): 
    3322        return True 
    3423 
     
    3625        raise NotImplementedError() 
    3726 
    3827    def title(self): 
    39         return self.field.verbose_name 
     28        raise NotImplementedError() 
     29     
     30    def get_query_set(self, cl, qs): 
     31        return qs 
     32         
    4033 
    4134    def output(self, cl): 
    4235        t = [] 
     
    5144            t.append('</ul>\n\n') 
    5245        return mark_safe("".join(t)) 
    5346 
    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) 
     47class FieldFilterSpec(FilterSpec): 
     48    field_filter_specs = [] 
     49    def __init__(self, request, params, model, model_admin, field): 
     50        super(FieldFilterSpec, self).__init__(request, params, model, model_admin) 
     51        self.field = field 
     52         
     53    def register(cls, test, factory): 
     54        cls.field_filter_specs.append((test, factory)) 
     55    register = classmethod(register) 
     56 
     57    def title(self): 
     58        return self.field.verbose_name 
     59     
     60    def create(cls, request, params, model, model_admin, field): 
     61        for test, factory in cls.field_filter_specs: 
     62            if test(field): 
     63                return factory(request, params, model, model_admin, field) 
     64    create = classmethod(create) 
     65 
     66class RelatedFilterSpec(FieldFilterSpec): 
     67    def __init__(self, request, params, model, model_admin, f): 
     68        super(RelatedFilterSpec, self).__init__(f, request, params, model, model_admin, field) 
    5769        if isinstance(f, models.ManyToManyField): 
    5870            self.lookup_title = f.rel.to._meta.verbose_name 
    5971        else: 
     
    7890                   'query_string': cl.get_query_string({self.lookup_kwarg: pk_val}), 
    7991                   'display': val} 
    8092 
    81 FilterSpec.register(lambda f: bool(f.rel), RelatedFilterSpec) 
     93FieldFilterSpec.register(lambda f: bool(f.rel), RelatedFilterSpec) 
    8294 
    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
     95class ChoicesFilterSpec(FieldFilterSpec): 
     96    def __init__(self, request, params, model, model_admin, f): 
     97        super(ChoicesFilterSpec, self).__init__(request, params, model, model_admin, f
    8698        self.lookup_kwarg = '%s__exact' % f.name 
    8799        self.lookup_val = request.GET.get(self.lookup_kwarg, None) 
    88100 
     
    95107                    'query_string': cl.get_query_string({self.lookup_kwarg: k}), 
    96108                    'display': v} 
    97109 
    98 FilterSpec.register(lambda f: bool(f.choices), ChoicesFilterSpec) 
     110FieldFilterSpec.register(lambda f: bool(f.choices), ChoicesFilterSpec) 
    99111 
    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
     112class DateFieldFilterSpec(FieldFilterSpec): 
     113    def __init__(self, request, params, model, model_admin, f): 
     114        super(DateFieldFilterSpec, self).__init__(request, params, model, model_admin, f
    103115 
    104116        self.field_generic = '%s__' % self.field.name 
    105117 
     
    130142                   'query_string': cl.get_query_string(param_dict, [self.field_generic]), 
    131143                   'display': title} 
    132144 
    133 FilterSpec.register(lambda f: isinstance(f, models.DateField), DateFieldFilterSpec) 
     145FieldFilterSpec.register(lambda f: isinstance(f, models.DateField), DateFieldFilterSpec) 
    134146 
    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
     147class BooleanFieldFilterSpec(FieldFilterSpec): 
     148    def __init__(self, request, params, model, model_admin, f): 
     149        super(BooleanFieldFilterSpec, self).__init__(request, params, model, model_admin, f
    138150        self.lookup_kwarg = '%s__exact' % f.name 
    139151        self.lookup_kwarg2 = '%s__isnull' % f.name 
    140152        self.lookup_val = request.GET.get(self.lookup_kwarg, None) 
     
    153165                   'query_string': cl.get_query_string({self.lookup_kwarg2: 'True'}, [self.lookup_kwarg]), 
    154166                   'display': _('Unknown')} 
    155167 
    156 FilterSpec.register(lambda f: isinstance(f, models.BooleanField) or isinstance(f, models.NullBooleanField), BooleanFieldFilterSpec) 
     168FieldFilterSpec.register(lambda f: isinstance(f, models.BooleanField) or isinstance(f, models.NullBooleanField), BooleanFieldFilterSpec) 
    157169 
    158170# This should be registered last, because it's a last resort. For example, 
    159171# if a field is eligible to use the BooleanFieldFilterSpec, that'd be much 
    160172# 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
     173class AllValuesFilterSpec(FieldFilterSpec): 
     174    def __init__(self, request, params, model, model_admin, f): 
     175        super(AllValuesFilterSpec, self).__init__(request, params, model, model_admin, f
    164176        self.lookup_val = request.GET.get(f.name, None) 
    165177        self.lookup_choices = model_admin.queryset(request).distinct().order_by(f.name).values(f.name) 
    166178 
     
    176188            yield {'selected': self.lookup_val == val, 
    177189                   'query_string': cl.get_query_string({self.field.name: val}), 
    178190                   'display': val} 
    179 FilterSpec.register(lambda f: True, AllValuesFilterSpec) 
     191FieldFilterSpec.register(lambda f: True, AllValuesFilterSpec) 
  • django/contrib/admin/views/main.py

    old new  
    11from django.contrib.admin.filterspecs import FilterSpec 
     2from django.contrib.admin.filterspecs import FieldFilterSpec 
    23from django.contrib.admin.options import IncorrectLookupParameters 
    34from django.contrib.admin.util import quote 
    45from django.core.paginator import Paginator, InvalidPage 
     
    6263        if ERROR_FLAG in self.params: 
    6364            del self.params[ERROR_FLAG] 
    6465 
     66        self.filter_specs, self.has_filters = self.get_filters(request) 
     67         
    6568        self.order_field, self.order_type = self.get_ordering() 
    6669        self.query = request.GET.get(SEARCH_VAR, '') 
    6770        self.query_set = self.get_query_set() 
    6871        self.get_results(request) 
    6972        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)) 
    70         self.filter_specs, self.has_filters = self.get_filters(request) 
     73 
    7174        self.pk_attname = self.lookup_opts.pk.attname 
    7275 
    7376    def get_filters(self, request): 
    7477        filter_specs = [] 
    7578        if self.list_filter: 
    76             filter_fields = [self.lookup_opts.get_field(field_name) for field_name in self.list_filter] 
    77             for f in filter_fields: 
    78                 spec = FilterSpec.create(f, request, self.params, self.model, self.model_admin) 
     79            for item in self.list_filter: 
     80                if callable(item): 
     81                    spec = item(request, self.params, self.model, self.model_admin) 
     82                else: 
     83                    if type(item) is tuple: 
     84                        (name, factory) = item 
     85                    else: 
     86                        name = item 
     87                        factory = FieldFilterSpec.create 
     88                    f = self.lookup_opts.get_field(name) 
     89                    spec = factory(request, self.params, self.model, self.model_admin, f) 
    7990                if spec and spec.has_output(): 
    8091                    filter_specs.append(spec) 
    8192        return filter_specs, bool(filter_specs) 
     
    172183 
    173184    def get_query_set(self): 
    174185        qs = self.root_query_set 
     186         
     187        # let every filter do its filtering 
     188        for filter_spec in self.filter_specs: 
     189            tqs = filter_spec.get_query_set(self, qs) 
     190            if tqs != None: qs = tqs 
     191         
    175192        lookup_params = self.params.copy() # a dictionary of the query string 
    176193        for i in (ALL_VAR, ORDER_VAR, ORDER_TYPE_VAR, SEARCH_VAR, IS_POPUP_VAR): 
    177194            if i in lookup_params: 
     
    181198                # 'key' will be used as a keyword argument later, so Python 
    182199                # requires it to be a string. 
    183200                del lookup_params[key] 
    184                 lookup_params[smart_str(key)] = value 
     201                key = smart_str(key) 
     202                lookup_params[key] = value 
    185203 
     204            from django.db.models.sql.constants import LOOKUP_SEP 
     205            try: 
     206                f = self.opts.get_field(key.split(LOOKUP_SEP)[0]) 
     207            except models.FieldDoesNotExist: 
     208                del lookup_params[key] 
     209                continue 
     210 
    186211            # if key ends with __in, split parameter into separate values 
    187212            if key.endswith('__in'): 
    188213                lookup_params[key] = value.split(',')