Django 1.3或以下版本的 Django 管理中的自定义过滤器

如何将自定义过滤器添加到 django 管理员(出现在模型仪表板右侧的过滤器) ?我知道基于这个模型的一个字段添加一个过滤器很容易,但是像下面这样的“计算”字段怎么样:

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??
48348 次浏览

不幸的是,您不能。目前非字段项不能用作 list _ filter 条目。

注意,即使管理类是一个字段,它也不会工作,因为单项元组需要一个逗号: ('is_live',)

你必须编写一个自定义的 FilterSpec (不能在任何地方记录)。 看看这个例子:

Http://www.djangosnippets.org/snippets/1051/

感谢吉皮罗蒂诺给我的推动,以正确的方向实施这一点。

我注意到这个问题的代码使用了一个日期时间来计算它的生存时间。所以我使用了 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 中,并将其导入到要添加筛选器的模型中

用户免费向一些国家提供商品,我想过滤这些国家:

所有 -所有国家,是的-免邮费,没有-收费邮费。

这个问题的主要答案不适合我(Django 1.3) ,我想是因为在 __init__方法中没有提供 field_path参数。它也是 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))

在自我。链接,我们提供字母。用于为每个可能的过滤器构造像 ?postage__exact=0这样的 HTTP 查询字符串。过滤器 我觉得是累积的,所以如果以前有一个“否”的请求,现在我们有一个“是”的请求,我们必须删除 “没有”疑问句。self.removes指定每个查询需要删除的内容。choices方法构造查询字符串,说明选择了哪个过滤器,并设置过滤器的显示名称。

只是一个旁注: 您可以像下面这样更容易地使用 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

不是最佳方式(CPU 智能) ,但简单,将工作,所以我这样做(为我的小数据库)。我的姜戈版本是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)

这里的关键思想是通过 电话函数访问 QuerySet 中的自定义字段。

下面是答案,并实现了自定义过滤器,尽可能简单,这可能有所帮助

Django 管理日期范围过滤器