Django & Tangos

Baby steps to a lovely web

Custom filter on django admin

,

We had to have a custom queryset for a model in Admin so we could have only the active records.
A teammate got it work by doing a new FilterSpec and I had to use it in another model.

First of all, create the FilterSpec for your admin:

customfilterspec.py

# -*- coding: utf-8 -*-

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

class CustomFilterSpec(FilterSpec):
    """
    Na verdade este é um FilterSpec para passar Queryset diferente do default do campo, por exemplo, filtrado 
    """
    def __init__(self, f, request, params, model, model_admin):
        super(CustomFilterSpec, self).__init__(f, request, params, model, model_admin)
        self.lookup_val = request.GET.get(f.name, None)
        if isinstance(f, models.ManyToManyField):
            self.lookup_title = f.rel.to._meta.verbose_name
        else:
            self.lookup_title = f.verbose_name
        
        # Queryset padrão
        qs = f.rel.to._default_manager.all()
        
        # Verifica se as opções do admin possuem um queryset para este campo
        qs_dict = getattr(model_admin, 'custom_filter_spec', None)
        if qs_dict and f.name in qs_dict:
            qs = qs_dict[f.name]
        
        self.lookup_choices = qs.all()

    def title(self):
        return self.lookup_title

    def choices(self, cl):
        yield {'selected': self.lookup_val is None,
               'query_string': cl.get_query_string({}, [self.field.name]),
               'display': All}
        for inst in self.lookup_choices:
            val = smart_unicode(inst.pk)
            yield {'selected': self.lookup_val == val,
                   'query_string': cl.get_query_string({self.field.name: val}),
                   'display': smart_unicode(inst)}

# O ideal seria usar o método register, mas o filtro ficaria por último e não seria utilizável.
# Então é preciso registrá-lo como primeiro.
#FilterSpec.register(lambda f: bool(f.rel and hasattr(f, 'custom_filter_spec')), CustomFilterSpec)
FilterSpec.filter_specs.insert(0, (lambda f: bool(f.rel and hasattr(f, 'custom_filter_spec')), CustomFilterSpec))



Save the file and put it somewhere in your app to import later

It must be loaded at least once so the filter will be added to the list, I recommend put it in an admin.py file.

so, on top of your admin.py:

from utils.customfilterspec import CustomFilterSpec



Then you must prepare your model to assure that the field can use FilterSpec¹

So, in your model do:

class MyModel(models.Model):
   created_by = models.ForeignKey(User)
   ...
   created_by.custom_filter_spec = True




And finally, in your Admin class you must include this field as a list_filter and give a resultset to it:

class MyAdminOptions(ModelAdmin):
   ...
   list_filter = ('created_by',)
   custom_filter_spec = {'created_by': User.objects.filter(is_staff=True).order_by('username')}



So now, in my example, I can filter only by staff user in my admin page.

Hope it helps

obs:
1: This models stuff is set in CustomFilterSpec. I believe it could work without this annoying step, just by changing the filter class, but I did not try.

cx_Oracle and Apache revisedFirefox redirects to localhost.com

Comments

Unregistered user Tuesday, March 16, 2010 11:17:12 AM

Py writes: Hi, thanks for this post. There are a little mistake in your CustomFilterSpec class, (line 37) 'display': All All needs quotes (line 37) 'display': 'All'

Unregistered user Monday, June 14, 2010 12:19:16 PM

Nick Z writes: Is it possible to filter objects being related by some other object? Like in photologue, there's Photo class and Gallery class. Gallery has ManyToMany field, listing Photos in a gallery. Is it possible to filter Photos by Gallery in that case?

Unregistered user Tuesday, November 16, 2010 6:06:28 PM

Anonymous writes: Great, that piece of code really rocks !

Unregistered user Thursday, June 2, 2011 11:34:20 AM

eng. Ilian Iliev writes: Really helpful. This gives me the idea how to filter the options in admin list view filter(for a ForeignKey field). You may check it at http://ilian.i-n-i.org/django-models-foreignkey-and-custom-admin-filters/

Unregistered user Sunday, October 9, 2011 9:31:19 AM

Anonymous writes: It's good to always write comments in English

Unregistered user Tuesday, November 1, 2011 2:40:54 AM

Anonymous writes: Work like charm!!! Thank you very much :)

Write a comment

New comments have been disabled for this post.