Login

Filter changelist by a numeric field using a number of common value ranges

Author:
asfaltboy
Posted:
September 3, 2015
Language:
Python
Version:
1.4
Tags:
filter admin field range
Score:
1 (after 1 ratings)

How to use

Use this admin filter together with a numeric field to allow filtering changlist by field values range (in this case, age groups):

For example, to group customers by age groups:

class Customer(models.Model):
    # ...
    age = models.IntegerField()
    age.list_lookup_range = (
        (None, _('All')),
        ([0, 2], '0-2'),
        ([2, 4], '2-4'),
        ([4, 18], '4-18'),
        ([18, 65], '18-65'),
        ([65, None], '65+'),
    ))

class CustomerAdmin(admin.ModelAdmin):
    list_filter = [('age', ValueRangeFilter), ]

Inspiration

This snippet (for django < 1.4) inspired me to make this work for newer django versions.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
from django.contrib import admin
from django.utils.translation import ugettext_lazy as _


class ValueRangeFilter(admin.FieldListFilter):
    def __init__(self, field, *args, **kwargs):
        self.lookup_kwarg = '%s__range' % kwargs['field_path']
        self.lookup_choices = getattr(field, 'lookup_range', (
            # default range options
            (None, _('All')),
            ([0, 100], '0-100'),
            ([100, 300], '100-300'),
            ([300, 1000], '300-1000'),
            ([1000, 10000], '1000-10000'),
            ([10000, None], '10000+'),
        ))
        super(ValueRangeFilter, self).__init__(field, *args, **kwargs)

    def expected_parameters(self):
        return [self.lookup_kwarg]

    def get_value_range(self):
        if not self.used_parameters.get(self.lookup_kwarg):
            return None
        return [int(n.strip("' ")) if n.strip("' ") != 'None' else None
                for n in self.used_parameters[self.lookup_kwarg].strip(
                    '[]').split(',')]

    def choices(self, cl):
        for lookup, title in self.lookup_choices:
            yield {
                'selected': self.get_value_range() == lookup,
                'query_string': cl.get_query_string({
                    self.lookup_kwarg: lookup,
                }, []),
                'display': title,
            }

    def queryset(self, request, queryset):
        if not self.used_parameters:
            return queryset
        values_range = self.get_value_range()
        if not values_range:
            return queryset
        min_val, max_val = values_range
        if min_val and max_val:
            query = {'%s__range' % self.field_path:
                     (min_val, max_val)}
        elif min_val:
            query = {'%s__gt' % self.field_path: min_val}
        elif max_val:
            query = {'%s__lt' % self.field_path: max_val}
        else:
            query = {}
        return queryset.filter(**query)

More like this

Comments

roolze (on September 4, 2015):

Works fine, very nice. In your example you are using the wrong attribute name ("list_lookup_range" instead" of "lookup_range").

Cheers Roland

#

Please login first before commenting.