Default filter in Django admin

前端 未结 15 1740
有刺的猬
有刺的猬 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:18

    Here's the Cleanest version I was able to generate of a filter with a redefined 'All' and a Default value that is selected.

    If shows me by default the Trips currently happening.

    class HappeningTripFilter(admin.SimpleListFilter):
        """
        Filter the Trips Happening in the Past, Future or now.
        """
        default_value = 'now'
        title = 'Happening'
        parameter_name = 'happening'
    
        def lookups(self, request, model_admin):
            """
            List the Choices available for this filter.
            """
            return (
                ('all', 'All'),
                ('future', 'Not yet started'),
                ('now', 'Happening now'),
                ('past', 'Already finished'),
            )
    
        def choices(self, changelist):
            """
            Overwrite this method to prevent the default "All".
            """
            value = self.value() or self.default_value
            for lookup, title in self.lookup_choices:
                yield {
                    'selected': value == force_text(lookup),
                    'query_string': changelist.get_query_string({
                        self.parameter_name: lookup,
                    }, []),
                    'display': title,
                }
    
        def queryset(self, request, queryset):
            """
            Returns the Queryset depending on the Choice.
            """
            value = self.value() or self.default_value
            now = timezone.now()
            if value == 'future':
                return queryset.filter(start_date_time__gt=now)
            if value == 'now':
                return queryset.filter(start_date_time__lte=now, end_date_time__gte=now)
            if value == 'past':
                return queryset.filter(end_date_time__lt=now)
            return queryset.all()
    
    0 讨论(0)
  • 2020-11-27 11:18

    Created a reusable Filter sub-class, inspired by some of the answers here (mostly Greg's).

    Advantages:

    Reusable - Pluggable in any standard ModelAdmin classes

    Extendable - Easy to add additional/custom logic for QuerySet filtering

    Easy to use - In its most basic form, only one custom attribute and one custom method need to be implemented (apart from those required for SimpleListFilter subclassing)

    Intuitive admin - The "All" filter link is working as expected; as are all the others

    No redirects - No need to inspect GET request payload, agnostic of HTTP_REFERER (or any other request related stuff, in its basic form)

    No (changelist) view manipulation - And no template manipulations (god forbid)

    Code:

    (most of the imports are just for type hints and exceptions)

    from typing import List, Tuple, Any
    
    from django.contrib.admin.filters import SimpleListFilter
    from django.contrib.admin.options import IncorrectLookupParameters
    from django.contrib.admin.views.main import ChangeList
    from django.db.models.query import QuerySet
    from django.utils.encoding import force_str
    from django.utils.translation import gettext_lazy as _
    from django.core.exceptions import ValidationError
    
    
    class PreFilteredListFilter(SimpleListFilter):
    
        # Either set this or override .get_default_value()
        default_value = None
    
        no_filter_value = 'all'
        no_filter_name = _("All")
    
        # Human-readable title which will be displayed in the
        # right admin sidebar just above the filter options.
        title = None
    
        # Parameter for the filter that will be used in the URL query.
        parameter_name = None
    
        def get_default_value(self):
            if self.default_value is not None:
                return self.default_value
            raise NotImplementedError(
                'Either the .default_value attribute needs to be set or '
                'the .get_default_value() method must be overridden to '
                'return a URL query argument for parameter_name.'
            )
    
        def get_lookups(self) -> List[Tuple[Any, str]]:
            """
            Returns a list of tuples. The first element in each
            tuple is the coded value for the option that will
            appear in the URL query. The second element is the
            human-readable name for the option that will appear
            in the right sidebar.
            """
            raise NotImplementedError(
                'The .get_lookups() method must be overridden to '
                'return a list of tuples (value, verbose value).'
            )
    
        # Overriding parent class:
        def lookups(self, request, model_admin) -> List[Tuple[Any, str]]:
            return [(self.no_filter_value, self.no_filter_name)] + self.get_lookups()
    
        # Overriding parent class:
        def queryset(self, request, queryset: QuerySet) -> QuerySet:
            """
            Returns the filtered queryset based on the value
            provided in the query string and retrievable via
            `self.value()`.
            """
            if self.value() is None:
                return self.get_default_queryset(queryset)
            if self.value() == self.no_filter_value:
                return queryset.all()
            return self.get_filtered_queryset(queryset)
    
        def get_default_queryset(self, queryset: QuerySet) -> QuerySet:
            return queryset.filter(**{self.parameter_name: self.get_default_value()})
    
        def get_filtered_queryset(self, queryset: QuerySet) -> QuerySet:
            try:
                return queryset.filter(**self.used_parameters)
            except (ValueError, ValidationError) as e:
                # Fields may raise a ValueError or ValidationError when converting
                # the parameters to the correct type.
                raise IncorrectLookupParameters(e)
    
        # Overriding parent class:
        def choices(self, changelist: ChangeList):
            """
            Overridden to prevent the default "All".
            """
            value = self.value() or force_str(self.get_default_value())
            for lookup, title in self.lookup_choices:
                yield {
                    'selected': value == force_str(lookup),
                    'query_string': changelist.get_query_string({self.parameter_name: lookup}),
                    'display': title,
                }
    

    Full usage example:

    from django.contrib import admin
    from .models import SomeModelWithStatus
    
    
    class StatusFilter(PreFilteredListFilter):
        default_value = SomeModelWithStatus.Status.FOO
        title = _('Status')
        parameter_name = 'status'
    
        def get_lookups(self):
            return SomeModelWithStatus.Status.choices
    
    
    @admin.register(SomeModelWithStatus)
    class SomeModelAdmin(admin.ModelAdmin):
        list_filter = (StatusFilter, )
    

    Hope this helps somebody; feedback always appreciated.

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

    I know this question is quite old now, but it's still valid. I believe this is the most correct way of doing this. It's essentially the same as Greg's method, but formulated as an extendible class for easy re-use.

    from django.contrib.admin import SimpleListFilter
    from django.utils.encoding import force_text
    from django.utils.translation import ugettext as _
    
    class DefaultListFilter(SimpleListFilter):
        all_value = '_all'
    
        def default_value(self):
            raise NotImplementedError()
    
        def queryset(self, request, queryset):
            if self.parameter_name in request.GET and request.GET[self.parameter_name] == self.all_value:
                return queryset
    
            if self.parameter_name in request.GET:
                return queryset.filter(**{self.parameter_name:request.GET[self.parameter_name]})
    
            return queryset.filter(**{self.parameter_name:self.default_value()})
    
        def choices(self, cl):
            yield {
                'selected': self.value() == self.all_value,
                'query_string': cl.get_query_string({self.parameter_name: self.all_value}, []),
                'display': _('All'),
            }
            for lookup, title in self.lookup_choices:
                yield {
                    'selected': self.value() == force_text(lookup) or (self.value() == None and force_text(self.default_value()) == force_text(lookup)),
                    'query_string': cl.get_query_string({
                        self.parameter_name: lookup,
                    }, []),
                    'display': title,
                }
    
    class StatusFilter(DefaultListFilter):
        title = _('Status ')
        parameter_name = 'status__exact'
    
        def lookups(self, request, model_admin):
            return ((0,'activate'), (1,'pending'), (2,'rejected'))
    
        def default_value(self):
            return 1
    
    class MyModelAdmin(admin.ModelAdmin):
        list_filter = (StatusFilter,)
    
    0 讨论(0)
提交回复
热议问题