Django 1.3 或更低版本的 Django Admin 中的自定义过滤器
Posted
技术标签:
【中文标题】Django 1.3 或更低版本的 Django Admin 中的自定义过滤器【英文标题】:Custom Filter in Django Admin on Django 1.3 or below 【发布时间】:2010-11-02 18:40:30 【问题描述】:如何向 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 src="/media/img/admin/icon-yes.gif"/> """
else:
return """ <img 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??
【问题讨论】:
其他人已经说过这个功能在主干(1.4 dev)中。更多信息:release note 和 documentation。 这里有一个更好的文档链接;扩展 SimpleListFilter 是要走的路。 FilterSpecs 已过期。 docs.djangoproject.com/en/dev/ref/contrib/admin/… 见下面的matley answer,带有官方文档的链接。 【参考方案1】:感谢 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中,然后在你想要添加过滤器的模型中导入
【讨论】:
您能否详细说明您使用此代码所做的最后一部分? Rosarch,最后一行代码在 django 中注册了 is_live_filter,然后在模型类的 models.py 中让我们说文章,你有一个名为 publish_date 的字段,你可以调用 publish_date.is_live_filter 最后一行 filter_specs.insert 非常重要,否则您的自定义过滤器可能不会显示,而是显示该字段类型的内置过滤器规格之一. (我一开始没有正确阅读答案,并且正在使用 .register 方法,就像内置的 filterspecs 使用一样!) 注意:在 Django 1.4 中,filterspecs(一直是内部黑客)已被重构为 ListFilter 并提供更清晰的自定义路径:请参阅 code.djangoproject.com/ticket/5833【参考方案2】:你必须编写一个自定义的 FilterSpec(不是任何地方的文档)。 看这里的例子:
http://www.djangosnippets.org/snippets/1051/
【讨论】:
【参考方案3】:在当前的 django 开发版本中,支持自定义过滤器:https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_filter
【讨论】:
更具体一点:docs.djangoproject.com/en/dev/ref/contrib/admin/… @Webthusiast 随意编辑任何答案以改进它。在这种情况下,我已经将您更具体的 URL 合并到了答案中。【参考方案4】:很遗憾,你不能。目前非字段项不能作为list_filter条目。
请注意,即使它是一个字段,您的管理类也不会起作用,因为单项元组需要一个逗号:('is_live',)
【讨论】:
FWIW,#5833 的修复现在在 django 1.4 的 django 主干中【参考方案5】:只是一个旁注:您可以像这样更轻松地在 Django admin 上使用默认刻度:
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
【讨论】:
【参考方案6】:不是最佳方式(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 中的自定义字段。
【讨论】:
也许不是最优化的方式,但一种简单且有效的方式。它让我头疼。【参考方案7】:用户向部分国家免费提供商品。我想过滤这些国家:
所有 - 所有国家,是 - 免邮费,否 - 收取邮费。
我认为这个问题的主要答案对我不起作用(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 我们提供字典。用于为每个可能的过滤器构造 HTTP 查询字符串,例如 ?postage__exact=0
。过滤器我认为是累积的,所以如果之前有一个“否”的请求,而现在我们有一个“是”的请求,我们必须删除
“否”查询。 self.removes
指定每个查询需要删除的内容。 choices
方法构造查询字符串,说明选择了哪个过滤器并设置过滤器的显示名称。
【讨论】:
【参考方案8】:这是答案并尽可能简单地实现自定义过滤器,这可能会有所帮助
Django admin date range filter
【讨论】:
以上是关于Django 1.3 或更低版本的 Django Admin 中的自定义过滤器的主要内容,如果未能解决你的问题,请参考以下文章
将旧 (Django 0.97) 模型数据导入/迁移到 Django 1.8 或更高版本
django 找不到新的 sqlite 版本? (需要 SQLite 3.8.3 或更高版本(找到 3.7.17))
Django - 安装 mysqlclient 错误:需要 mysqlclient 1.3.13 或更高版本;你有 0.9.3
在 Django 1.8 或更高版本中填充时出现“模型尚未加载”错误