Django Simple Search

This django form class creates a search form for a model and generates a query from the submitted data.

To use, extend the BaseSearchForm class, defining a Meta class with the following values:

base_qs

The queryset to search, ie. MyModel.objects

search_fields

The fields to search, using the same syntax as the django admin directive of the same name, ie.

  • 'fieldname' - field must contain the search string
  • '^fieldname' - field must start with the search string
  • '=fieldname' - field must exactly equal the search string
  • '@fieldname' - performs a fulltext search (mysql/postgres only) on field

Related model fields can also be searched, ie 'mycategory__name' will search the name field on the mycategory model.

fulltext_indexes

Defines the mysql fulltext indexes to search against. If this is set, and the database engine is mysql, this setting will override search_fields. See "Fulltext searches" below for more info.

Additional search fields

Custom fields can be added to filter the results - by default, these perform an exact-match search on the model field of the same name. A prepare_FIELDNAME method can be defined for each custom field for advanced queries - this method should return a Q instance for addition to the overall query. See below for an example.

Fulltext searches

If the database engine in use is mysql, and fulltext_indexes is set, the form will perform a fulltext search instead of constructing a query from search_fields. The format of fulltext_indexes should be a list of tuples of the format (fields, weighting), where fields is a comma-separated list of field names corresponding to the index, and weighting is an integer. In the example below, matches on 'name' are weighted double what those on the rest of the index are, meaning they should come up first in the results.

Note that only one fulltext index is required, because the 'name' match can be made against the second index.

Example form class (myapp/forms.py)

from django.db.models import Q
from simple_search import BaseSearchForm
from myapp.models import MyModel, MyCategory


class MyModelSearchForm(BaseSearchForm):
    class Meta:
        base_qs = MyModel.objects
        search_fields = ('^name', 'description', 'specifications', '=id') 

        # assumes a fulltext index has been defined on the fields
        # 'name,description,specifications,id'
        fulltext_indexes = (
            ('name', 2), # name matches are weighted higher
            ('name,description,specifications,id', 1),
        )

    """ 
    A custom addition - the absence of a prepare_category method means
    the query will search for an exact match on this field.
    """
    category = forms.ModelChoiceField(
        queryset = MyCategory.objects.all(),
        required = False
    )

    """ 
    This field creates a custom query addition via the prepare_start_date
    method.
    """
    start_date = forms.DateField(
        required = False,
        input_formats = ('%Y-%m-%d',),
    )
    def prepare_start_date(self):
        if self.cleaned_data['start_date']:
            return Q(creation_date__gte=self.cleaned_data['start_date'])
        else:
            return ""

Example use in a view (myapp/views.py)

from django.shortcuts import render_to_response
from django.template import RequestContext
from myapp.forms import MyModelSearchForm

def search(request):

    if request.GET:
        form = MyModelSearchForm(request.GET)
        if form.is_valid():
            results = form.get_result_queryset()
        else:
            results = []
    else:
        form = MyModelSearchForm()
        results = []


    return render_to_response(
        'search.html',
        RequestContext(request, {
            'form': form,
            'results': results,
        })
    )

Demo

You can try it out on this site, either from the search box in the footer or here.

Get the code

You can download or view the code on github.


Loading