Django

Code

Ticket #5833: 5833-against-9836-new-proper.patch

File 5833-against-9836-new-proper.patch, 14.3 kB (added by eandre@gmail.com, 1 year ago)

New patch against r9836

  • django/contrib/admin/validation.py

    old new  
    88from django.forms.models import BaseModelForm, BaseModelFormSet, fields_for_model 
    99from django.contrib.admin.options import flatten_fieldsets, BaseModelAdmin 
    1010from django.contrib.admin.options import HORIZONTAL, VERTICAL 
     11from django.contrib.admin.filterspecs import FilterSpec, FieldFilterSpec 
    1112 
    1213__all__ = ['validate'] 
    1314 
     
    5051            fetch_attr(cls, model, opts, 'list_display_links[%d]' % idx, field) 
    5152            if field not in cls.list_display: 
    5253                raise ImproperlyConfigured("'%s.list_display_links[%d]'" 
    53                         "refers to '%s' which is not defined in 'list_display'." 
     54                        " refers to '%s' which is not defined in 'list_display'." 
    5455                        % (cls.__name__, idx, field)) 
    5556 
    5657    # list_filter 
    5758    if hasattr(cls, 'list_filter'): 
    5859        check_isseq(cls, 'list_filter', cls.list_filter) 
    59         for idx, field in enumerate(cls.list_filter): 
    60             get_field(cls, model, opts, 'list_filter[%d]' % idx, field) 
     60        for idx, item in enumerate(cls.list_filter): 
     61            if callable(item): 
     62                # Make sure the item is not FieldFilterSpec or a subclass thereof 
     63                # since it has a different __init__ signature, which leads to  
     64                # strange exceptions if not caught here 
     65                if not issubclass(item, FilterSpec) or issubclass(item, FieldFilterSpec): 
     66                    raise ImproperlyConfigured("'%s.list_filter[%d]' is '%s' which is not of type FilterSpec." 
     67                        % (cls.__name__, idx, item)) 
     68                else: 
     69                    try: 
     70                        field_name, factory = item 
     71                    except (TypeError, ValueError): 
     72                        field_name = item 
     73                    else: 
     74                        if not issubclass(factory, FieldFilterSpec): 
     75                            raise ImproperlyConfigured("'%s.list_filter[%d][1]'" 
     76                                " refers to '%s' which is not of type FieldFilterSpec." 
     77                                % (cls.__name__, idx, factory.__name__)) 
     78                    # Validate field 
     79                    get_field(cls, model, opts, 'list_filter[%d]' % idx, field) 
    6180 
    6281    # list_per_page = 100 
    6382    if hasattr(cls, 'list_per_page') and not isinstance(cls.list_per_page, int): 
  • django/contrib/admin/__init__.py

    old new  
    11from django.contrib.admin.options import ModelAdmin, HORIZONTAL, VERTICAL 
    22from django.contrib.admin.options import StackedInline, TabularInline 
    33from django.contrib.admin.sites import AdminSite, site 
     4from django.contrib.admin.filterspecs import FilterSpec, FieldFilterSpec 
    45 
    56# A flag to tell us if autodiscover is running.  autodiscover will set this to 
    67# True while running, and False when it finishes. 
  • 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 
    2119 
    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  
    3220    def has_output(self): 
    3321        return True 
    3422 
    3523    def choices(self, cl): 
    3624        raise NotImplementedError() 
    37  
     25         
    3826    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 [] 
    4041 
    4142    def output(self, cl): 
    4243        t = [] 
     
    5051                     choice['display'])) 
    5152            t.append('</ul>\n\n') 
    5253        return mark_safe("".join(t)) 
     54         
     55class 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) 
    5379 
    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
     80class RelatedFilterSpec(FieldFilterSpec): 
     81    def __init__(self, request, params, model, model_admin, f): 
     82        super(RelatedFilterSpec, self).__init__(request, params, model, model_admin, f
    5783        if isinstance(f, models.ManyToManyField): 
    5884            self.lookup_title = f.rel.to._meta.verbose_name 
    5985        else: 
     
    77103                   'query_string': cl.get_query_string({self.lookup_kwarg: pk_val}), 
    78104                   'display': val} 
    79105 
    80 FilterSpec.register(lambda f: bool(f.rel), RelatedFilterSpec
     106FieldFilterSpec.register(lambda f: bool(f.rel), RelatedFilterSpec, False
    81107 
    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
     108class ChoicesFilterSpec(FieldFilterSpec): 
     109    def __init__(self, request, params, model, model_admin, f): 
     110        super(ChoicesFilterSpec, self).__init__(request, params, model, model_admin, f
    85111        self.lookup_kwarg = '%s__exact' % f.name 
    86112        self.lookup_val = request.GET.get(self.lookup_kwarg, None) 
    87113 
     
    94120                    'query_string': cl.get_query_string({self.lookup_kwarg: k}), 
    95121                    'display': v} 
    96122 
    97 FilterSpec.register(lambda f: bool(f.choices), ChoicesFilterSpec
     123FieldFilterSpec.register(lambda f: bool(f.choices), ChoicesFilterSpec, False
    98124 
    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
     125class DateFieldFilterSpec(FieldFilterSpec): 
     126    def __init__(self, request, params, model, model_admin, f): 
     127        super(DateFieldFilterSpec, self).__init__(request, params, model, model_admin, f
    102128 
    103129        self.field_generic = '%s__' % self.field.name 
    104130 
     
    120146            (_('This year'), {'%s__year' % self.field.name: str(today.year)}) 
    121147        ) 
    122148 
    123     def title(self): 
    124         return self.field.verbose_name 
    125  
    126149    def choices(self, cl): 
    127150        for title, param_dict in self.links: 
    128151            yield {'selected': self.date_params == param_dict, 
    129152                   'query_string': cl.get_query_string(param_dict, [self.field_generic]), 
    130153                   'display': title} 
    131154 
    132 FilterSpec.register(lambda f: isinstance(f, models.DateField), DateFieldFilterSpec
     155FieldFilterSpec.register(lambda f: isinstance(f, models.DateField), DateFieldFilterSpec, False
    133156 
    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
     157class BooleanFieldFilterSpec(FieldFilterSpec): 
     158    def __init__(self, request, params, model, model_admin, f): 
     159        super(BooleanFieldFilterSpec, self).__init__(request, params, model, model_admin, f
    137160        self.lookup_kwarg = '%s__exact' % f.name 
    138161        self.lookup_kwarg2 = '%s__isnull' % f.name 
    139162        self.lookup_val = request.GET.get(self.lookup_kwarg, None) 
    140163        self.lookup_val2 = request.GET.get(self.lookup_kwarg2, None) 
    141164 
    142     def title(self): 
    143         return self.field.verbose_name 
    144  
    145165    def choices(self, cl): 
    146166        for k, v in ((_('All'), None), (_('Yes'), '1'), (_('No'), '0')): 
    147167            yield {'selected': self.lookup_val == v and not self.lookup_val2, 
     
    152172                   'query_string': cl.get_query_string({self.lookup_kwarg2: 'True'}, [self.lookup_kwarg]), 
    153173                   'display': _('Unknown')} 
    154174 
    155 FilterSpec.register(lambda f: isinstance(f, models.BooleanField) or isinstance(f, models.NullBooleanField), BooleanFieldFilterSpec
     175FieldFilterSpec.register(lambda f: isinstance(f, models.BooleanField) or isinstance(f, models.NullBooleanField), BooleanFieldFilterSpec, False
    156176 
    157177# This should be registered last, because it's a last resort. For example, 
    158178# if a field is eligible to use the BooleanFieldFilterSpec, that'd be much 
    159179# 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
     180class AllValuesFilterSpec(FieldFilterSpec): 
     181    def __init__(self, request, params, model, model_admin, f): 
     182        super(AllValuesFilterSpec, self).__init__(request, params, model, model_admin, f
    163183        self.lookup_val = request.GET.get(f.name, None) 
    164184        self.lookup_choices = model_admin.queryset(request).distinct().order_by(f.name).values(f.name) 
    165185 
    166     def title(self): 
    167         return self.field.verbose_name 
    168  
    169186    def choices(self, cl): 
    170187        yield {'selected': self.lookup_val is None, 
    171188               'query_string': cl.get_query_string({}, [self.field.name]), 
     
    175192            yield {'selected': self.lookup_val == val, 
    176193                   'query_string': cl.get_query_string({self.field.name: val}), 
    177194                   'display': val} 
    178 FilterSpec.register(lambda f: True, AllValuesFilterSpec
     195FieldFilterSpec.register(lambda f: True, AllValuesFilterSpec, False
  • django/contrib/admin/views/main.py

    old new  
    1 from django.contrib.admin.filterspecs import FilterSpec 
     1from django.contrib.admin.filterspecs import FilterSpec, FieldFilterSpec 
    22from django.contrib.admin.options import IncorrectLookupParameters 
    33from django.contrib.admin.util import quote 
    44from django.core.paginator import Paginator, InvalidPage 
     
    6262        if ERROR_FLAG in self.params: 
    6363            del self.params[ERROR_FLAG] 
    6464 
     65        self.filter_specs, self.has_filters = self.get_filters(request) 
    6566        self.order_field, self.order_type = self.get_ordering() 
    6667        self.query = request.GET.get(SEARCH_VAR, '') 
    6768        self.query_set = self.get_query_set() 
    6869        self.get_results(request) 
    6970        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) 
    7171        self.pk_attname = self.lookup_opts.pk.attname 
    7272 
    7373    def get_filters(self, request): 
    7474        filter_specs = [] 
    7575        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) 
     76            for item in self.list_filter: 
     77                if callable(item): 
     78                    spec = item(request, self.params, self.model, self.model_admin) 
     79                else: 
     80                    try: 
     81                        field_name, factory = item 
     82                    except (TypeError, ValueError): 
     83                        field_name, factory = item, FieldFilterSpec.create 
     84                    field = self.lookup_opts.get_field(field_name) 
     85                    spec = factory(request, self.params, self.model, 
     86                                   self.model_admin, field) 
    7987                if spec and spec.has_output(): 
    8088                    filter_specs.append(spec) 
    8189        return filter_specs, bool(filter_specs) 
     
    183191            # if key ends with __in, split parameter into separate values 
    184192            if key.endswith('__in'): 
    185193                lookup_params[key] = value.split(',') 
     194                 
     195        # Let every filter spec modify the qs and params to its liking 
     196        for filter_spec in self.filter_specs: 
     197            new_qs = filter_spec.get_query_set(self, qs) 
     198            if new_qs: 
     199                qs = new_qs 
     200                # Only consume params if we got a new queryset 
     201                for param in filter_spec.consumed_params(): 
     202                    try: 
     203                        del lookup_params[param] 
     204                    except KeyError: 
     205                        pass 
    186206 
    187207        # Apply lookup parameters from the query string. 
    188208        try: