Django admin中的默认filter
我怎样才能从“全部”更改默认筛选器的select? 我有一个名为status
的字段有三个值: activate
, pending
和rejected
。 当我在Django admin中使用list_filter
时,filter默认设置为'All',但是我希望默认设置为pending。
为了实现这一点, 并在侧边栏中有一个可用的“全部”链接(即一个显示所有,而不是显示挂起),你需要创build一个自定义列表filter,从django.contrib.admin.filters.SimpleListFilter
inheritance并在默认情况下过滤“挂起”。 应该沿着这些线路工作:
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(感谢西蒙)
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
来select“全部”。
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)
我知道这个问题现在已经很老了,但它仍然有效。 我相信这是做这件事最正确的方法。 这与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,)
这是我使用redirect的通用解决scheme,它只是检查是否有任何GET参数,如果不存在,那么它redirect与默认的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设置,所以它会使用默认参数和redirect。 这对我来说很好,当你点击pipe理filter时,它工作的很好。
更新 :
为了解决这个问题,我最终编写了一个简化changelist_viewfunction的自定义filter函数。 这是filter:
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现在只传递默认参数。 这个想法是摆脱了genericsfilter的能力,通过使用没有得到参数查看所有。 要查看我为此分配的所有状态= 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 )
请注意,如果不是预先selectfilter值,而是希望始终在将数据显示在pipe理员中之前过滤数据,则应该重写ModelAdmin.queryset()
方法。
你可以简单地使用return queryset.filter()
或者if self.value() is None
和SimpleListFilter的Override方法
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, }
我知道这不是最好的解决scheme,但我更改了pipe理模板中的index.html,第25行和第37行,如下所示:
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>
我不得不做一个修改,让过滤正常工作。 以前的解决scheme为我加载页面时。 如果执行了“操作”,则筛选器将返回到“全部”,而不是我的默认设置。 此解决scheme使用默认filter加载pipe理员更改页面,但也会在页面上发生其他活动时维护filter更改或当前filter。 我没有testing过所有的情况,但实际上它可能会限制默认filter的设置,只有在页面加载时才会发生。
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)
Greg的答案使用DjangoChoices,Python> = 2.5,当然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]
感谢格雷格的好解决scheme!
有点偏离主题,但我寻找类似的问题,导致我在这里。 我正在寻找一个date的默认查询(即如果没有提供input,只显示对象的timestamp
“今天”),这使问题复杂一点。 这是我想出来的:
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), )
这是默认的DateFieldListFilter
一个简单覆盖。 通过设置self.date_params
,它可以确保filter下拉将更新为与self.used_parameters
相匹配的任何选项。 出于这个原因,你必须确保self.used_parameters
正是这些下拉选项中的一个(即,使用“Today”或“Last 7 Days”来查找date_params
是什么,并构buildself.used_parameters
来匹配那些)。
这是build立与Django 1.4.10的工作
这可能是一个古老的线程,但认为我会添加我的解决scheme,因为我找不到更好的答案谷歌search。
做什么(不知道它的Deminic Rodger或ha22109)是否在ModelAdmin中为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)
然后我们需要创build一个自定义的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