Mar 24

Rendering search results in Django

This weekend I wrote a Django tag & filter module for rendering search results. The primary feature is truncating a body of text to show the search terms in context, just like Google shows you an excerpt. A decent text indexer will normally provide this, but most of my projects aren’t big enough to justify setting one up, so I make do with normal database queries.

Examples and documentation are on the snippet page, but here’s how I actually use it in my latest project. I have some flatpages storing documentation and other help for the web site, and a search form just for these pages. The following code can practically be dropped into any Django project using flatpages.

The view looks like this:

from django.contrib.flatpages.models import FlatPage
from django.db.models import Q

def help_search(request):
    query_string = request.GET.get('q', "")
    search_terms = query_string.split()
    results = FlatPage.objects.filter(url__startswith="/help/")

    if search_terms:
        query = Q()
        for term in search_terms:
            query &= Q(content__icontains=term) | Q(title__icontains=term)
        results = results.filter(query)

    return render_to_response("flatpages/search.html",
        {'query': query_string, 'terms': search_terms, 'results': results}
    )

And the relevant portion of flatpages/search.html looks like this:

{% load search %}

<h1>Search Results</h1>
<h2>
    {% with results|length as result_count %}
    {{ result_count|default:"No" }} page{{ result_count|pluralize }} found
    {% if terms %}for &ldquo;{{ query }}&rdquo;{% endif %}
    {% endwith %}
</h2>
<ul id="results">
    {% for page in results %}
    <li>
        {% searchexcerpt terms 6 as content %} {# set `content` object #}
            {{ page.content|striptags }}
        {% endsearchexcerpt %}
        {% highlight terms as title %} {# set `title` object #}
            {{ page.title }}
        {% endhighlight %}
        <h3>
            <a href="{{ page.get_absolute_url }}">{{ title.highlighted }}</a>
            {% if terms %}{% with content.hits|add:title.hits as hits %}
            <span class="count">{{ hits }} hit{{ hits|pluralize }}</span>
            {% endwith %}{% endif %}
        </h3>
        <p class="context">{{ content.excerpt|highlight:terms }}</p>
    </li>
    {% endfor %}
</ul>

Finally, here are some screenshots of the results:

Search screenshot 1Search screenshot 2Search screenshot 3

Page 1 of 1