Default filter in Django admin

前端 未结 15 1739
有刺的猬
有刺的猬 2020-11-27 10:49

How can I change the default filter choice from \'ALL\'? I have a field named as status which has three values: activate, pending and

相关标签:
15条回答
  • 2020-11-27 11:12

    A slight improvement on Greg's answer using DjangoChoices, Python >= 2.5 and of course Django >= 1.4.

    from django.utils.translation import ugettext_lazy as _
    from django.contrib.admin import SimpleListFilter
    
    class OrderStatusFilter(SimpleListFilter):
        title = _('Status')
    
        parameter_name = 'status__exact'
        default_status = OrderStatuses.closed
    
        def lookups(self, request, model_admin):
            return (('all', _('All')),) + OrderStatuses.choices
    
        def choices(self, cl):
            for lookup, title in self.lookup_choices:
                yield {
                    'selected': self.value() == lookup if self.value() else lookup == self.default_status,
                    'query_string': cl.get_query_string({self.parameter_name: lookup}, []),
                    'display': title,
                }
    
        def queryset(self, request, queryset):
            if self.value() in OrderStatuses.values:
                return queryset.filter(status=self.value())
            elif self.value() is None:
                return queryset.filter(status=self.default_status)
    
    
    class Admin(admin.ModelAdmin):
        list_filter = [OrderStatusFilter] 
    

    Thanks to Greg for the nice solution!

    0 讨论(0)
  • 2020-11-27 11:13

    A bit off-topic but my search for a similar question led me here. I was looking to have a default query by a date (ie if no input is provided, show only objects with timestamp of 'Today'), which complicates the question a bit. Here is what I came up with:

    from django.contrib.admin.options import IncorrectLookupParameters
    from django.core.exceptions import ValidationError
    
    class TodayDefaultDateFieldListFilter(admin.DateFieldListFilter):
        """ If no date is query params are provided, query for Today """
    
        def queryset(self, request, queryset):
            try:
                if not self.used_parameters:
                    now = datetime.datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
                    self.used_parameters = {
                        ('%s__lt' % self.field_path): str(now + datetime.timedelta(days=1)),
                        ('%s__gte' % self.field_path): str(now),
                    }
                    # Insure that the dropdown reflects 'Today'
                    self.date_params = self.used_parameters
                return queryset.filter(**self.used_parameters)
            except ValidationError, e:
                raise IncorrectLookupParameters(e)
    
    class ImagesAdmin(admin.ModelAdmin):
        list_filter = (
            ('timestamp', TodayDefaultDateFieldListFilter),
        )
    

    This is a simple override of the default DateFieldListFilter. By setting self.date_params, it insures that the filter dropdown will update to whatever option matches the self.used_parameters. For this reason, you must insure that the self.used_parameters are exactly what would be used by one of those dropdown selections (ie, find out what the date_params would be when using the 'Today' or 'Last 7 Days' and construct the self.used_parameters to match those).

    This was built to work with Django 1.4.10

    0 讨论(0)
  • 2020-11-27 11:14

    I had to make a modification to get filtering to work correctly. The previous solution worked for me when the page loaded. If an 'action' was performed, the filter went back to 'All' and not my default. This solution loads the admin change page with the default filter, but also maintains filter changes or the current filter when other activity occurs on the page. I haven't tested all cases, but in reality it may be limiting the setting of a default filter to occur only when the page loads.

    def changelist_view(self, request, extra_context=None):
        default_filter = False
    
        try:
            ref = request.META['HTTP_REFERER']
            pinfo = request.META['PATH_INFO']
            qstr = ref.split(pinfo)
            querystr = request.META['QUERY_STRING']
    
            # Check the QUERY_STRING value, otherwise when
            # trying to filter the filter gets reset below
            if querystr is None:
                if len(qstr) < 2 or qstr[1] == '':
                    default_filter = True
        except:
            default_filter = True
    
        if default_filter:
            q = request.GET.copy()
            q['registered__isnull'] = 'True'
            request.GET = q
            request.META['QUERY_STRING'] = request.GET.urlencode()
    
        return super(MyAdmin, self).changelist_view(request, extra_context=extra_context)
    
    0 讨论(0)
  • 2020-11-27 11:15

    This may be an old thread, but thought I would add my solution as I couldn't find better answers on google searches.

    Do what (not sure if its Deminic Rodger, or ha22109) answered in the ModelAdmin for changelist_view

    class MyModelAdmin(admin.ModelAdmin):   
        list_filter = (CustomFilter,)
    
        def changelist_view(self, request, extra_context=None):
    
            if not request.GET.has_key('decommissioned__exact'):
    
                q = request.GET.copy()
                q['decommissioned__exact'] = 'N'
                request.GET = q
                request.META['QUERY_STRING'] = request.GET.urlencode()
            return super(MyModelAdmin,self).changelist_view(request, extra_context=extra_context)
    

    Then we need to create a custom SimpleListFilter

    class CustomFilter(admin.SimpleListFilter):
        title = 'Decommissioned'
        parameter_name = 'decommissioned'  # i chose to change it
    
    def lookups(self, request, model_admin):
        return (
            ('All', 'all'),
            ('1', 'Decommissioned'),
            ('0', 'Active (or whatever)'),
        )
    
    # had to override so that we could remove the default 'All' option
    # that won't work with our default filter in the ModelAdmin class
    def choices(self, cl):
        yield {
            'selected': self.value() is None,
            'query_string': cl.get_query_string({}, [self.parameter_name]),
            # 'display': _('All'),
        }
        for lookup, title in self.lookup_choices:
            yield {
                'selected': self.value() == lookup,
                'query_string': cl.get_query_string({
                    self.parameter_name: lookup,
                }, []),
                'display': title,
            }
    
    def queryset(self, request, queryset):
        if self.value() == '1':
            return queryset.filter(decommissioned=1)
        elif self.value() == '0':
            return queryset.filter(decommissioned=0)
        return queryset
    
    0 讨论(0)
  • 2020-11-27 11:17

    In order to achieve this and have a usable 'All' link in your sidebar (ie one that shows all rather than showing pending), you'd need to create a custom list filter, inheriting from django.contrib.admin.filters.SimpleListFilter and filtering on 'pending' by default. Something along these lines should work:

    from datetime import date
    
    from django.utils.translation import ugettext_lazy as _
    from django.contrib.admin import SimpleListFilter
    
    class StatusFilter(SimpleListFilter):
        title = _('Status')
    
        parameter_name = 'status'
    
        def lookups(self, request, model_admin):
            return (
                (None, _('Pending')),
                ('activate', _('Activate')),
                ('rejected', _('Rejected')),
                ('all', _('All')),
            )
    
        def choices(self, cl):
            for lookup, title in self.lookup_choices:
                yield {
                    'selected': self.value() == lookup,
                    'query_string': cl.get_query_string({
                        self.parameter_name: lookup,
                    }, []),
                    'display': title,
                }
    
        def queryset(self, request, queryset):
            if self.value() in ('activate', 'rejected'):
                return queryset.filter(status=self.value())    
            elif self.value() == None:
                return queryset.filter(status='pending')
    
    
    class Admin(admin.ModelAdmin): 
        list_filter = [StatusFilter] 
    

    EDIT: Requires Django 1.4 (thanks Simon)

    0 讨论(0)
  • 2020-11-27 11:17

    I know that is not the best solution, but i changed the index.html in the admin template, line 25 and 37 like this:

    25: <th scope="row"><a href="{{ model.admin_url }}{% ifequal model.name "yourmodelname" %}?yourflag_flag__exact=1{% endifequal %}">{{ model.name }}</a></th>

    37: <td><a href="{{ model.admin_url }}{% ifequal model.name "yourmodelname" %}?yourflag__exact=1{% endifequal %}" class="changelink">{% trans 'Change' %}</a></td>

    0 讨论(0)
提交回复
热议问题