Django admin中的默认过滤器

ha2*_*109 88 python django-admin

如何更改"ALL"的默认过滤器选项?我有一个名为领域status它有三个值:activate,pendingrejected.当我list_filter在Django admin中使用时,默认情况下,过滤器设置为"全部",但我希望默认情况下将其设置为挂起.

Gre*_*reg 92

为了实现这一点在侧边栏中有一个可用的"全部"链接(即显示所有链接而不是显示待处理的链接),您需要创建一个自定义列表过滤器,django.contrib.admin.filters.SimpleListFilter默认情况下继承并过滤"待定".这些方面的东西应该有效:

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] 
Run Code Online (Sandbox Code Playgroud)

编辑:需要Django 1.4(感谢Simon)

  • 这是所有人中最干净的解决方案,但它拥有最少的upvotes ......它需要Django 1.4,尽管这应该是现在给定的. (3认同)
  • 刚试过,它也适用于Django 1.8. (3认同)
  • 请注意,如果您无法覆盖解决方案中的“choices”方法,它会令人烦恼地继续在选项列表的顶部添加自己的 **_All_** 选项。 (3认同)
  • 该解决方案具有小缺点.当过滤器为空(实际使用'待定'​​过滤器)时,如果**show_full_result_count**为True(默认情况下),则Django 1.8错误地确定完整结果计数并且不显示结果计数. - (2认同)

ha2*_*109 46

class MyModelAdmin(admin.ModelAdmin):   

    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)
Run Code Online (Sandbox Code Playgroud)

  • 此解决方案的缺点是尽管"全部"选项仍显示在UI中,但选择它仍然会应用默认过滤. (17认同)

iri*_*ent 18

通过ha22109上面的答案并修改为允许通过比较HTTP_REFERER和选择"全部" PATH_INFO.

class MyModelAdmin(admin.ModelAdmin):

    def changelist_view(self, request, extra_context=None):

        test = request.META['HTTP_REFERER'].split(request.META['PATH_INFO'])

        if test[-1] and not test[-1].startswith('?'):
            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)
Run Code Online (Sandbox Code Playgroud)

  • 这对我来说是因为HTTP_REFERER并不总是存在.我做了'referer = request.META.get('HTTP_REFERER',''); test = referer.split(request.META ['PATH_INFO'])` (3认同)
  • 这很好用。不过,`has_key()` 已被弃用,取而代之的是 `key in d`。但我知道你刚刚从 ha22109 的回答中得到了答案。一个问题:当你可以使用`request.path_info`(较短)时,为什么要使用`request.META['PATH_INFO']`? (2认同)

And*_*ows 17

我知道这个问题现在很老了,但它仍然有效.我相信这是最正确的做法.它与Greg的方法基本相同,但是作为一个易于重复使用的可扩展类.

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,)
Run Code Online (Sandbox Code Playgroud)


Joh*_*alt 9

创建了一个可重用的 Filter 子类,灵感来自这里的一些答案(主要是 Greg 的)。

优点:

可重用- 可插入任何标准ModelAdmin

可扩展- 轻松添加QuerySet用于过滤的附加/自定义逻辑

易于使用- 在最基本的形式中,只需要实现一个自定义属性和一个自定义方法(除了 SimpleListFilter 子类化所需的那些之外)

直观的管理- “全部”过滤器链接按预期工作;和其他人一样

无重定向- 无需检查GET请求有效负载,不可知HTTP_REFERER(或任何其他请求相关的内容,以其基本形式)

没有(更改列表)视图操作- 也没有模板操作(上帝禁止)

代码:

(大部分imports 只是用于类型提示和异常)

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,
            }
Run Code Online (Sandbox Code Playgroud)

完整用法示例:

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, )
Run Code Online (Sandbox Code Playgroud)

希望这对某人有帮助;反馈总是受到赞赏。


rad*_*tek 7

这是我使用重定向的通用解决方案,它只检查是否有任何GET参数,如果不存在,则使用默认的get参数重定向.我也有一个list_filter设置,所以它选择了它并显示默认值.

from django.shortcuts import redirect

class MyModelAdmin(admin.ModelAdmin):   

    ...

    list_filter = ('status', )

    def changelist_view(self, request, extra_context=None):
        referrer = request.META.get('HTTP_REFERER', '')
        get_param = "status__exact=5"
        if len(request.GET) == 0 and '?' not in referrer:
            return redirect("{url}?{get_parms}".format(url=request.path, get_parms=get_param))
        return super(MyModelAdmin,self).changelist_view(request, extra_context=extra_context)
Run Code Online (Sandbox Code Playgroud)

唯一需要注意的是当你直接进入"?"页面时 在url中,没有设置HTTP_REFERER,因此它将使用默认参数和重定向.这对我来说很好,当你点击管理过滤器时效果很好.

更新:

为了解决这个问题,我最终编写了一个自定义过滤器功能,简化了changelist_view功能.这是过滤器:

class MyModelStatusFilter(admin.SimpleListFilter):
    title = _('Status')
    parameter_name = 'status'

    def lookups(self, request, model_admin):  # Available Values / Status Codes etc..
        return (
            (8, _('All')),
            (0, _('Incomplete')),
            (5, _('Pending')),
            (6, _('Selected')),
            (7, _('Accepted')),
        )

    def choices(self, cl):  # Overwrite this method to prevent the default "All"
        from django.utils.encoding import force_text
        for lookup, title in self.lookup_choices:
            yield {
                'selected': self.value() == force_text(lookup),
                'query_string': cl.get_query_string({
                    self.parameter_name: lookup,
                }, []),
                'display': title,
            }

    def queryset(self, request, queryset):  # Run the queryset based on your lookup values
        if self.value() is None:
            return queryset.filter(status=5)
        elif int(self.value()) == 0:
            return queryset.filter(status__lte=4)
        elif int(self.value()) == 8:
            return queryset.all()
        elif int(self.value()) >= 5:
            return queryset.filter(status=self.value())
        return queryset.filter(status=5)
Run Code Online (Sandbox Code Playgroud)

而如果不存在,changelist_view现在只传递默认参数.我们的想法是通过使用no get参数来摆脱泛型过滤器功能以查看所有内容.要查看为此目的分配状态= 8的所有内容:

class MyModelAdmin(admin.ModelAdmin):   

    ...

    list_filter = ('status', )

    def changelist_view(self, request, extra_context=None):
        if len(request.GET) == 0:
            get_param = "status=5"
            return redirect("{url}?{get_parms}".format(url=request.path, get_parms=get_param))
        return super(MyModelAdmin, self).changelist_view(request, extra_context=extra_context)
Run Code Online (Sandbox Code Playgroud)


小智 5

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 )

        if len( qstr ) < 2:
            default_filter = True
    except:
        default_filter = True

    if default_filter:
        q = request.GET.copy()
        q['registered__exact'] = '1'
        request.GET = q
        request.META['QUERY_STRING'] = request.GET.urlencode()

    return super( InterestAdmin, self ).changelist_view( request, extra_context = extra_context )
Run Code Online (Sandbox Code Playgroud)