I have a really simple blog application and I want to add a really simple search feature to it.
There are 3 key fields to my model.
class BlogPost(models
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.
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).
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.
If you want it to work just like the admin, you could try my mini-lib Django simple search. It's basically a port of the admin search functionality. Install it with pip:
pip install django-simple-search
and use it like:
from simple_search import search_filter
from .models import BlogPost
search_fields = ['^title', 'intro', 'content']
query = 'search term here'
posts = BlogPost.objects.filter(search_filter(search_fields, query))
I also wrote a blog post about it: https://gregbrown.co/projects/django-simple-search