2011-11-21

Custom Admin Filter Specification

Let's say we have a model Project with age groups as boolean fields:

class Project(models.Model):
    # ...
    age_group_0_5 = models.BooleanField(u"From 0 to 5 years")
    age_group_6_12 = models.BooleanField(u"From 6 to 12 years")
    age_group_13_18 = models.BooleanField(u"From 13 to 18 years")
    age_group_19_27 = models.BooleanField(u"Fron 19 to 27 years")
    # ...

In order to list them as one selection in the admin list filter, we need a specific filter specification. Let's create it in the admin.py:

from django.contrib.admin.filterspecs import FilterSpec

class AgeChoicesFilterSpec(FilterSpec):
    def title(self):
        return u"Age group"
        
    def __init__(self, f, request, params, model, model_admin,
                 field_path=None):
        super(AgeChoicesFilterSpec, self).__init__(f, request, params, model,
                                                model_admin,
                                                field_path=field_path)
        self.request = request
        
    def choices(self, cl):
        yield {'selected': not(
                    self.request.GET.get('age_group_0_5', None) or 
                    self.request.GET.get('age_group_6_12', None) or 
                    self.request.GET.get('age_group_13_18', None) or 
                    self.request.GET.get('age_group_19_27', None) 
                    ),
               'query_string': cl.get_query_string(
                               {},
                               ['age_group_0_5', 'age_group_6_12', 'age_group_13_18', 'age_group_19_27']),
               'display': u'All'}
        yield {'selected': self.request.GET.get('age_group_0_5', None) == 'True',
               'query_string': cl.get_query_string(
                               {'age_group_0_5': 'True'},
                               ['age_group_6_12', 'age_group_13_18', 'age_group_19_27']),
               'display': u"From 0 to 5 years"}
        yield {'selected': self.request.GET.get('age_group_6_12', None) == 'True',
               'query_string': cl.get_query_string(
                               {'age_group_6_12': 'True'},
                               ['age_group_0_5', 'age_group_13_18', 'age_group_19_27']),
               'display': u"From 6 to 12 years"}
        yield {'selected': self.request.GET.get('age_group_13_18', None) == 'True',
               'query_string': cl.get_query_string(
                               {'age_group_13_18': 'True'},
                               ['age_group_0_5', 'age_group_6_12', 'age_group_19_27']),
               'display': u"From 13 to 18 years"}
        yield {'selected': self.request.GET.get('age_group_19_27', None) == 'True',
               'query_string': cl.get_query_string(
                               {'age_group_19_27': 'True'},
                               ['age_group_0_5', 'age_group_6_12', 'age_group_13_18']),
               'display': u"From 19 to 27 years"}

FilterSpec.filter_specs.insert(0, (lambda f: f.name == 'age_group_0_5',
                               AgeChoicesFilterSpec))

Here the most important method of FilterSpec is choices. It defines the choices for the filter by dictionary with three keys:

  • selected is the condition when this choice should be selected.
  • query_string is the URL query string that will be formed to filter the list by. The query string is formed by get_query_string method of ChangeList instance. It takes two parameters: a dictionary of new parameters to add to the current query, and a list of parameters to remove from the current query.
  • display is the human-readable string which will be shown for this choice in the filter.

The new filter specification should be attached to a field. Here it will be applied to all fields with the name "age_group_0_5" and we have just one such field.

The last thing to do is to define admin options for the Project model:

from django.contrib import admin
from models import Project

class ProjectAdmin(admin.ModelAdmin):
    # ...
    list_filter = ("age_group_0_5", )
    # ...
    
admin.site.register(Project, ProjectAdmin)
Now you know how to create a custom admin filter specification for your model. :)

2011-11-11

Django CMS, Haystack, and Custom Plugins

When you need a full-text search for Django-CMS-based website, you can use Haystack and django-cms-search. The latter module ensures that all CMS Pages get indexed.

One important thing to mention is that if you use any custom Plugins, search_fields need to be defined for them, so that the Pages using them are indexed properly. For example, here is an Overview plugin which makes its title and description searchable:


from django.db import models
from django.utils.translation import ugettext_lazy as _
from cms.models import CMSPlugin

class Overview(CMSPlugin):
    title = models.CharField(_('Title'), max_length=200)
    description = models.TextField(_('Description'))
    url = models.URLField(_('url'), max_length=200, blank=True)
    
    search_fields = ("title", "description")
    
    def __unicode__(self):
        return self.title
        

For more information check the documentation online: