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

58๐Ÿ‘

โœ…

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

๐Ÿ‘คMark Ellul

23๐Ÿ‘

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

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

๐Ÿ‘คgpilotino

9๐Ÿ‘

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

๐Ÿ‘คmatley

3๐Ÿ‘

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

3๐Ÿ‘

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
๐Ÿ‘คgklka

3๐Ÿ‘

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.

2๐Ÿ‘

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.

๐Ÿ‘คpeter2108

1๐Ÿ‘

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

Django admin date range filter

๐Ÿ‘คJay Dave

Leave a comment