Simple search in Django

If you want a really simple search you can use icontains lookup and Q object:

from django.db.models import Q
results = BlogPost.objects.filter(Q(title__icontains=your_search_query) | Q(intro__icontains=your_search_query) | Q(content__icontains=your_search_query))

You should also note that Haystack doesn't have to be "hideously complicated". You can set up haystack with Whoosh backend in less then 15 minutes.

Update 2016: In version 1.10 Django added a full text search support (PostgreSQL only). An answer to the original question using the new module might look something like this:

from django.contrib.postgres.search import SearchVector

results = BlogPost.objects.annotate(
    search=SearchVector('title', 'intro', 'content'),
).filter(search=your_search_query)

The new full text search module contains a lot more features (for example sorting by relevancy), you can read about them in the documentation.


From the Django source:

# Apply keyword searches.
def construct_search(field_name):
    if field_name.startswith('^'):
        return "%s__istartswith" % field_name[1:]
    elif field_name.startswith('='):
        return "%s__iexact" % field_name[1:]
    elif field_name.startswith('@'):
        return "%s__search" % field_name[1:]
    else:
        return "%s__icontains" % field_name

if self.search_fields and self.query:
    for bit in self.query.split():
        or_queries = [models.Q(**{construct_search(str(field_name)): bit}) for field_name in self.search_fields]
        qs = qs.filter(reduce(operator.or_, or_queries))
    for field_name in self.search_fields:
        if '__' in field_name:
            qs = qs.distinct()
            break

Clearly, it uses the database options to perform search. If nothing else, you should be able to reuse some of the code from it.

So says the documentation too: http://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.search_fields

The full text search, however, uses the MySQL index (only if you are using MySQL).


You would use the __search operator. It's documented in the Django QuerySet API Reference. There's also istartswith, which does a case-insensitive starts-with search.

Here's a working example (adapted from my own Django site):

def search(request):
    try:
        q = request.GET['q']
        posts = BlogPost.objects.filter(title__search=q) | \
                BlogPost.objects.filter(intro__search=q) | \
                BlogPost.objects.filter(content__search=q)
        return render_to_response('search/results.html', {'posts':posts, 'q':q})
    except KeyError:
        return render_to_response('search/results.html')

Note that __search is only available in MySQL and requires direct manipulation of the database to add the full-text index. See the MySQL documentation for additional details.

Tags:

Search

Django