如何向django admin(显示在模型仪表板右侧的过滤器)添加自定义过滤器?我知道很容易包含基于该模型的字段的过滤器,但是像这样的“计算”字段呢:
class NewsItem(models.Model):
headline = models.CharField(max_length=4096, blank=False)
byline_1 = models.CharField(max_length=4096, blank=True)
dateline = models.DateTimeField(help_text=_("date/time that appears on article"))
body_copy = models.TextField(blank=False)
when_to_publish = models.DateTimeField(verbose_name="When to publish", blank=True, null=True)
# HOW CAN I HAVE "is_live" as part of the admin filter? It's a calculated state!!
def is_live(self):
if self.when_to_publish is not None:
if ( self.when_to_publish < datetime.now() ):
return """ <img alt="True" src="/media/img/admin/icon-yes.gif"/> """
else:
return """ <img alt="False" src="/media/img/admin/icon-no.gif"/> """
is_live.allow_tags = True
class NewsItemAdmin(admin.ModelAdmin):
form = NewsItemAdminForm
list_display = ('headline', 'id', 'is_live')
list_filter = ('is_live') # how can i make this work??
答案 0 :(得分:57)
感谢gpilotino为我提供了实现这一目标的正确方向。
我注意到问题的代码是使用日期时间来确定它的实时。所以我使用了DateFieldFilterSpec并将其子类化。
from django.db import models
from django.contrib.admin.filterspecs import FilterSpec, ChoicesFilterSpec,DateFieldFilterSpec
from django.utils.encoding import smart_unicode
from django.utils.translation import ugettext as _
from datetime import datetime
class IsLiveFilterSpec(DateFieldFilterSpec):
"""
Adds filtering by future and previous values in the admin
filter sidebar. Set the is_live_filter filter in the model field attribute
'is_live_filter'. my_model_field.is_live_filter = True
"""
def __init__(self, f, request, params, model, model_admin):
super(IsLiveFilterSpec, self).__init__(f, request, params, model,
model_admin)
today = datetime.now()
self.links = (
(_('Any'), {}),
(_('Yes'), {'%s__lte' % self.field.name: str(today),
}),
(_('No'), {'%s__gte' % self.field.name: str(today),
}),
)
def title(self):
return "Is Live"
# registering the filter
FilterSpec.filter_specs.insert(0, (lambda f: getattr(f, 'is_live_filter', False),
IsLiveFilterSpec))
要使用,您可以将上述代码放入filters.py中,然后将其导入要添加过滤器的模型中
答案 1 :(得分:22)
你必须编写一个自定义FilterSpec(不是任何地方的文档)。 在这里看一个例子:
答案 2 :(得分:9)
在当前的django开发版中,支持自定义过滤器:https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_filter
答案 3 :(得分:3)
不幸的是,你不能。目前,非字段项目不能用作list_filter条目。
请注意,即使是一个字段,您的管理类也不会有用,因为单项元组需要逗号:('is_live',)
答案 4 :(得分:3)
只是旁注:您可以更轻松地使用Django管理员的默认刻度:
def is_live(self):
if self.when_to_publish is not None:
if ( self.when_to_publish < datetime.now() ):
return True
else:
return False
is_live.boolean = True
答案 5 :(得分:3)
不是最佳方式(CPU方式),但简单且可行,所以我这样做(对于我的小型数据库)。我的Django版本是1.6。
在admin.py中:
class IsLiveFilter(admin.SimpleListFilter):
title = 'Live'
parameter_name = 'islive'
def lookups(self, request, model_admin):
return (
('1', 'islive'),
)
def queryset(self, request, queryset):
if self.value():
array = []
for element in queryset:
if element.is_live.__call__() == True:
q_array.append(element.id)
return queryset.filter(pk__in=q_array)
...
class NewsItemAdmin(admin.ModelAdmin):
form = NewsItemAdminForm
list_display = ('headline', 'id', 'is_live')
list_filter = (IsLiveFilter)
这里的关键思想是通过 __ call __()函数访问QuerySet中的自定义字段。
答案 6 :(得分:2)
用户免费向一些国家/地区提供商品。我想过滤这些国家:
全部 - 所有国家/地区,是 - 免邮费,否 - 收取邮资费用。
这个问题的主要答案对我不起作用(Django 1.3)我认为因为field_path
方法中没有提供__init__
参数。它也是DateFieldFilterSpec
的子类。 postage
字段是FloatField
from django.contrib.admin.filterspecs import FilterSpec
class IsFreePostage(FilterSpec):
def __init__(self, f, request, params, model, model_admin, field_path=None):
super(IsFreePostage, self).__init__(f, request, params, model,
model_admin, field_path)
self.removes = {
'Yes': ['postage__gt'],
'No': ['postage__exact'],
'All': ['postage__exact', 'postage__gt'] }
self.links = (
('All', {}),
('Yes', {'postage__exact': 0}),
('No', {'postage__gt': 0}))
if request.GET.has_key('postage__exact'):
self.ttl = 'Yes'
elif request.GET.has_key('postage__gt'):
self.ttl = 'No'
else:
self.ttl = 'All'
def choices(self, cl):
for title, param_dict in self.links:
yield {'selected': title == self.ttl,
'query_string': cl.get_query_string(param_dict,
self.removes[title]),
'display': title}
def title(self):
return 'Free Postage'
FilterSpec.filter_specs.insert(0,
(lambda f: getattr(f, 'free_postage', False), IsFreePostage))
在self.links中,我们提供dicts。用于为每个可能的过滤器构建HTTP查询字符串,如?postage__exact=0
。过滤器我认为是累积的,所以如果之前有'否'请求,现在我们要求'是',我们必须删除
'不'查询。 self.removes
指定每个查询需要删除的内容。 choices
方法构造查询字符串,说明已选择哪个过滤器并设置过滤器的显示名称。
答案 7 :(得分:1)
以下是答案并尽可能简单地实现了自定义过滤器,这可能会有所帮助