我如何将默认的筛选选项从“所有”更改为其它选项?我有一个名为status
的字段,它有三个值:activate
,pending
和rejected
。当在Django管理后台使用list_filter
时,默认情况下筛选器设置为“全部”,但我想将其默认设置为“待处理”。
我如何将默认的筛选选项从“所有”更改为其它选项?我有一个名为status
的字段,它有三个值:activate
,pending
和rejected
。当在Django管理后台使用list_filter
时,默认情况下筛选器设置为“全部”,但我想将其默认设置为“待处理”。
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]
编辑:需要Django 1.4(感谢Simon)
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)
参考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)
referer = request.META.get('HTTP_REFERER', ''); test = referer.split(request.META['PATH_INFO'])
进行了处理。 - ben authorKeyError
异常,而如果你使用字典的get()
方法,你可以指定一个默认值。我指定了一个空字符串作为默认值,这样split()就不会抛出AttributeError
异常了。就是这样。 - ben authorhas_key()
已被弃用,推荐使用 key in d
。虽然我知道你只是从 ha22109 的答案中获取了代码。有一个问题:为什么要使用 request.META['PATH_INFO']
,而不是直接使用 request.path_info
(更短)? - Nick我知道这个问题现在已经很老了,但它仍然有效。我相信这是做这件事情最正确的方法。实质上,它与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,)
这是我的通用解决方案,使用重定向,它只检查是否存在任何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)
唯一的注意事项是,当您直接访问带有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)
现在的 changelist_view 只有在没有参数的情况下才传递默认参数。这样做的目的是通过不使用 GET 参数来消除通用筛选器查看所有内容的能力。为了查看所有内容,我将状态(status)赋值为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)
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 )
创建了一个可重用的过滤器子类,灵感来自于这里一些答案(主要是 Greg 的)。
可重用性 - 可插入任何标准的 ModelAdmin
类中
可扩展性 - 易于为 QuerySet
过滤添加额外/自定义逻辑
易于使用 - 在其最基本形式中,只需要实现一个自定义属性和一个自定义方法(除了 SimpleListFilter 子类化所需的属性之外)
直观的管理界面 - 所有其他筛选器链接都像预期的那样工作
无需重定向 - 无需检查 GET
请求有效负载,与 HTTP_REFERER
(或任何其他请求相关内容,在其基本形式中)无关
无视图操纵 - 也没有模板操纵(天佑我们)
(大多数 import
只是用于类型提示和异常处理)
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,
}
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, )
ModelAdmin.queryset()
方法。return queryset.filter()
或者 if self.value() is None
并重写 SimpleListFilter 的方法来实现。from django.utils.encoding import force_text
def choices(self, changelist):
for lookup, title in self.lookup_choices:
yield {
'selected': force_text(self.value()) == force_text(lookup),
'query_string': changelist.get_query_string(
{self.parameter_name: lookup}, []
),
'display': title,
}
使用DjangoChoices、Python >= 2.5和当然是Django >= 1.4,对Greg的答案进行了轻微改进。
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]
choices
方法,它将令人讨厌地继续在选择列表的顶部添加自己的**全部**选项。 - richard