views:

4003

answers:

3

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??
+1  A: 

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

Daniel Roseman
someday mabye, it's an open ticket http://code.djangoproject.com/ticket/5833
jobscry
+9  A: 

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

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

gpilotino
Related snippet: http://djangosnippets.org/snippets/1963/
Eddified
+7  A: 

Thanks to gpilotino for giving me the push into the write 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

Mark Ellul