Login

Alphabetic filter for admin

Author:
semente
Posted:
September 12, 2008
Language:
Python
Version:
1.0
Score:
9 (after 9 ratings)

This snippet is based on #748.

Adds filtering by first char (alphabetic style) of values in the admin filter sidebar. The example below results in this filter:

By name that starts with
 All
 A
 B
 G
 M
 X

urls.py example (only for register the filter):

import <your project>.admin.filterspecs

models.py example:

from django.db import models

class Person(models.Model):
    name = models.CharField(max_length=40)
    name.alphabetic_filter = True

admin.py example:

class Admin:
    list_filter = ['name']
 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
# Authors: Marinho Brandao <marinho at gmail.com>
#          Guilherme M. Gondim (semente) <semente at taurinus.org>
# File: <your project>/admin/filterspecs.py

from django.db import models
from django.contrib.admin.filterspecs import FilterSpec, ChoicesFilterSpec
from django.utils.encoding import smart_unicode
from django.utils.translation import ugettext as _

class AlphabeticFilterSpec(ChoicesFilterSpec):
    """
    Adds filtering by first char (alphabetic style) of values in the admin
    filter sidebar. Set the alphabetic filter in the model field attribute
    'alphabetic_filter'.

    my_model_field.alphabetic_filter = True
    """

    def __init__(self, f, request, params, model, model_admin):
        super(AlphabeticFilterSpec, self).__init__(f, request, params, model,
                                                   model_admin)
        self.lookup_kwarg = '%s__istartswith' % f.name
        self.lookup_val = request.GET.get(self.lookup_kwarg, None)
        values_list = model.objects.values_list(f.name, flat=True)
        # getting the first char of values
        self.lookup_choices = list(set(val[0] for val in values_list if val))
        self.lookup_choices.sort()

    def choices(self, cl):
        yield {'selected': self.lookup_val is None,
                'query_string': cl.get_query_string({}, [self.lookup_kwarg]),
                'display': _('All')}
        for val in self.lookup_choices:
            yield {'selected': smart_unicode(val) == self.lookup_val,
                    'query_string': cl.get_query_string({self.lookup_kwarg: val}),
                    'display': val.upper()}
    def title(self):
        return _('%(field_name)s that starts with') % \
            {'field_name': self.field.verbose_name}

# registering the filter
FilterSpec.filter_specs.insert(0, (lambda f: getattr(f, 'alphabetic_filter', False),
                                   AlphabeticFilterSpec))

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 10 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 2 weeks ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
  5. Help text hyperlinks by sa2812 1 year, 6 months ago

Comments

magicrebirth (on May 13, 2009):

Line 55 is better changed like this:

self.lookup_choices = list(set(val[0] for val in values_list if val))

BY adding 'if val' it prevents the routine to crash when indexing items that are blank!

#

semente (on May 29, 2009):

snippet updated.

magicrebirth, thanks for the tip.

#

bradmontgomery (on January 14, 2010):

Thanks! This is very helpful.

#

luc_j (on November 10, 2010):

Very helpful. I've made the following changes. I hope it helps

Avoid duplicates for lower and uppercase

self.lookup_choices = list(set(val.upper()[0] for val in values_list if val))

The register must be called explicitely

@classmethod
def register_filterspec(cls):
    """register the filter. To be called in the models.py"""
    FilterSpec.filter_specs.insert(0,
        (lambda f: getattr(f, 'alphabetic_filter', False), cls)
    )

#

ohnoimdead (on May 15, 2011):

Need to add field_path to init:

def __init__(self, f, request, params, model, model_admin, field_path=None):
    super(AlphabeticFilterSpec, self).__init__(f, request, params, model,
                                               model_admin,
                                               field_path=field_path)

#

martinvanbuuren (on October 10, 2011):

Solution to "'long' object is unsubscriptable"

Change

26 self.lookup_choices = list(set(val[0] for val in values_list if val))

to

26 self.lookup_choices = list(set(str(val)[0] for val in values_list if val))

<hr />

This issue rises at ForeignKeys. The solution returns the first character of the id's rather than an error message.

#

Please login first before commenting.