简体   繁体   English

在Django 1.3或更低版本的Django Admin中自定义过滤器

[英]Custom Filter in Django Admin on Django 1.3 or below

How can I add a custom filter to django admin (the filters that appear on the right side of a model dashboard)? 如何向django admin(显示在模型仪表板右侧的过滤器)添加自定义过滤器? I know its easy to include a filter based on a field of that model, but what about a "calculated" field like this: 我知道很容易包含一个基于该模型字段的过滤器,但是这样的“计算”字段呢:

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??

Thanks to gpilotino for giving me the push into the right direction for implementing this. 感谢gpilotino为我提供了实现这一目标的正确方向。

I noticed the question's code is using a datetime to figure out when its live . 我注意到问题的代码是使用日期时间来确定它的生效时间。 So I used the DateFieldFilterSpec and subclassed it. 所以我使用了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))

To use you can put the above code into a filters.py, and import it in the model you want to add the filter to 要使用,您可以将上面的代码放入filters.py中,然后将其导入要添加过滤器的模型中

you have to write a custom FilterSpec (not documentend anywhere). 你必须编写一个自定义FilterSpec(不是任何地方的文档)。 Look here for an example: 在这里看一个例子:

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

在当前的django开发版中,支持自定义过滤器: https//docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_filter

Just a sidenote: You can use the deafult ticks on Django admin more easily like this: 只是一个旁注:你可以更轻松地使用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

Not an optimal way (CPU-wise) but simple and will work, so I do it this way (for my small database). 不是一种最佳方式(CPU方式),但简单并且可以工作,所以我这样做(对于我的小型数据库)。 My Django version is 1.6. 我的Django版本是1.6。

In admin.py: 在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)

Key idea here is to access custom fields in a QuerySet via __call__() function. 这里的关键思想是通过__call __()函数访问QuerySet中的自定义字段。

You can't, unfortunately. 不幸的是,你不能。 Currently non-field items can not be used as list_filter entries. 目前,非字段项不能用作list_filter条目。

Note that your admin class wouldn't have worked even if it was a field, as a single-item tuple needs a comma: ('is_live',) 请注意,即使是一个字段,您的管理类也不会起作用,因为单项元组需要一个逗号:( ('is_live',)

The user supplies goods to some countries postage free. 用户免费向一些国家提供货物。 I wanted to filter those countries: 我想过滤这些国家/地区:

All - all countries, Yes - postage free, No - charged postage. 全部 - 所有国家, - 免邮费, 收取邮费。

The main answer for this question did not work for me (Django 1.3) I think because there was no field_path parameter provided in the __init__ method. 这个问题的主要答案对我不起作用(Django 1.3)我认为因为__init__方法中没有提供field_path参数。 Also it subclassed DateFieldFilterSpec . 它还DateFieldFilterSpec The postage field is a FloatField 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))

In self.links we supply dicts. 在self.links中,我们提供dicts。 used to construct HTTP query strings like ?postage__exact=0 for each of the possible filters. 用于为每个可能的过滤器构造HTTP查询字符串,如?postage__exact=0 Filters I think are cumulative so if there was a previous request for 'No' and now we have a request for 'Yes' we have to remove the 'No' query. 我认为过滤器是累积的,所以如果之前有'否'请求,现在我们要求'是',我们必须删除'否'查询。 self.removes specifies what needs to be removed for each query. self.removes指定每个查询需要删除的内容。 The choices method constructs the query strings, says which filter has been selected and sets the displayed name of the filter. choices方法构造查询字符串,说明已选择哪个过滤器并设置过滤器的显示名称。

Here is the answer and implemented the custom filter as simple as possible this might help 这是答案,并尽可能简单地实现自定义过滤器,这可能会有所帮助

Django admin date range filter Django管理日期范围过滤器

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM