在Django 1.3或更低版本的Django Admin中自定义filter
我如何添加一个自定义filter到Django的pipe理员(模型仪表板右侧出现的filter)? 我知道很容易包含一个基于该模型的字段的filter,但是像这样的“计算”字段呢?
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??
感谢gpilotino让我推动实现这一目标的正确方向。
我注意到问题的代码是使用date时间来确定它的生活时间。 所以我使用了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中,并将其导入到你想添加filter的模型中
你必须编写一个自定义的FilterSpec(不在任何地方文档)。 在这里寻找一个例子:
在当前的Django开发版本中,支持自定义filter: https : //docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_filter
不幸的是,你不能。 目前非字段项目不能用作list_filter条目。
请注意,即使它是一个字段,你的pipe理类也不会工作,因为单项元组需要一个逗号:( ('is_live',)
只是一个旁注:您可以更容易地使用Django admin上的deafult ticks:
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方式),但简单,将工作,所以我这样做(为我的小型数据库)。 我的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中的自定义字段。
用户向一些国家免费提供货物。 我想过滤这些国家:
所有的国家, 是的 – 邮费免费, 不收取邮费。
这个问题的主要答案不适用于我(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))
在self.links我们提供字典。 用于为每个可能的filter构造HTTP查询string,如?postage__exact=0
。 我认为filter是累计的,所以如果之前有一个“否”的请求,现在我们有一个“是”的请求,我们必须删除“否”查询。 self.removes
指定每个查询需要删除的内容。 choices
方法构造查询string,说明哪个filter已被选中,并设置filter的显示名称。
这是答案,并尽可能简单地实现了自定义filter,这可能会有所帮助
Django的pipe理date范围filter