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

发布于 2024-12-03 01:49:23 字数 1152 浏览 2 评论 0原文

如何向 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??

How can I add a custom filter to django admin (the filters that appear on the right side of a model dashboard)? 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??

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

帅冕 2024-12-10 01:49:23

感谢 gpilotino 为我指明了实现这一目标的正确方向。

我注意到问题的代码使用日期时间来确定其 live 时间。所以我使用了 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中,并将其导入到您想要添加过滤器的模型中

Thanks to gpilotino for giving me the push into the right direction for implementing this.

I noticed the question's code is using a datetime to figure out when its live . So I used the DateFieldFilterSpec and subclassed it.

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

呆萌少年 2024-12-10 01:49:23

你必须编写一个自定义的 FilterSpec (不在任何地方记录)。
请参阅此处的示例:

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

you have to write a custom FilterSpec (not documentend anywhere).
Look here for an example:

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

聊慰 2024-12-10 01:49:23

In current django development version there is the support for custom filters: https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_filter

物价感观 2024-12-10 01:49:23

不幸的是,你不能。目前非字段项不能用作list_filter条目。

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

You can't, unfortunately. Currently non-field items can not be used as list_filter entries.

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',)

一杆小烟枪 2024-12-10 01:49:23

只是旁注:您可以更轻松地使用 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

Just a sidenote: You can use the deafult ticks on Django admin more easily like this:

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
溺ぐ爱和你が 2024-12-10 01:49:23

这不是最佳方法(就 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 中的自定义字段。

Not an optimal way (CPU-wise) but simple and will work, so I do it this way (for my small database). My Django version is 1.6.

In 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.

靑春怀旧 2024-12-10 01:49:23

用户向某些国家免费提供商品。我想过滤这些国家/地区:

所有 - 所有国家/地区, - 免费邮资, - 收费邮资。

这个问题的主要答案对我来说不起作用(Django 1.3),我认为是因为 __init__ 方法中没有提供 field_path 参数。它还继承了 DateFieldFilterSpecpostage 字段是一个 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 方法构造查询字符串,说明已选择哪个过滤器并设置过滤器的显示名称。

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. Also it subclassed DateFieldFilterSpec. The postage field is a 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. used to construct HTTP query strings like ?postage__exact=0 for each of the possible filters. 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. The choices method constructs the query strings, says which filter has been selected and sets the displayed name of the filter.

旧梦荧光笔 2024-12-10 01:49:23

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

Django admin日期范围过滤器

Here is the answer and implemented the custom filter as simple as possible this might help

Django admin date range filter

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文