How to integrate haystack search with Django admin

Recently, I had to integrate Haystack with Django admin so as to be able to perform a full text search just via the admin. Looking around for resources on the internet, I came across this page in Haystack's docs. Turns out Haystack has provided a solution to ease the integration of Haystack with Django admin.

Well, sort of. You see, the solution they've provided in the docs is only partial. You can do exactly what the docs instruct, yet the example won't work.

The solution is to set the search_fields attribute in your admin class. The value of search_fields can be set to any field that is present in your model. Haystack's SearchModelAdmin class will override it and will search your search index's text field.

Below is an example that would provide a little more illustration to the solution:

# models.py

class Article(models.Model):
    title = models.CharField(max_length=100)
    body = models.TextField() # we want to search this field
    pub_date = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['-pub_date'] # show newest articles first

I'm not gonna show how to set up Haystack search index. This post is not about that. Hopefully, you've already done that. If not, you can read Haystack's docs about that.

Let's see the admin:

# admin.py

from django.contrib import admin
from haystack.admin import SearchModelAdmin
from .models import Article


class ArticleAdmin(SearchModelAdmin):
    # set the search_fields attribute to a field present in Article
    search_fields = ('title',) # or ('body',) it doesn't really matter

We have inherited our admin class from SearchModelAdmin class provided by Haystack. This will take the search query argument, search the index, then return the results.

That's it! Try searching for something in Django admin, it should be working (if it's not, have you updated or created a search index yet?).

Heads up

There are two downsides of integrating Haystack search with Django admin.

1. The checkboxes on the left of the changelist page table will not appear in the search results. I don't find this a big issue, though. If a find a solution to this, I will post it here.

2. The search results will not be ordered as expected. For example, we've ordered the Articles by pub_date in reverse order so that newest articles appear first. But this ordering won't apply to haystack's search results. They will appear, in this case, as oldest first.

There's a workaround for that. You might not like it. I don't like. Having said that, I used that workaround for the project since I was on a deadline. And now I'm too lazy to look for another solutions. Anyways, here it is:

Firstly, make sure that you've included the model field in your haystack search index class that you want to use for ordering. So in this case, that field is pub_date. What I mean is, you have to included pub_date in your search index if you want to use it for ordering. Example:

class ArticleIndex(indexes.SearchIndex, indexes.Indexable):
    text = indexes.CharField(document=True, use_template=True)
    pub_date = indexes.DateTimeField(model_attr='pub_date')

Now copy the haystack/admin.py file to your project or app's directory. Save it as search_admin.py. Open it in a text editor. You'll see a class defined as SearchChangeList. You'll have to change a line inside it. Find the following line in that class:

sqs = SearchQuerySet(self.haystack_connection).models(self.model).auto_query(request.GET[SEARCH_VAR]).load_all()

Now, all you have to do is append .order_by('field_name') to that line. In this case, .order_by('-pub_date') ("-" means reverse ordering). Example:

sqs = SearchQuerySet(self.haystack_connection
    ).models(self.model
    ).auto_query(request.GET[SEARCH_VAR]
    ).load_all().order_by('-pub-date')

Finally, in your admin.py file, you've to import the SearchModelAdmin from this file, instead of from haystack.admin.

from haystack.admin import SearchModelAdmin # Replace this

from search_admin import SearchModelAdmin # with this

I know, this workaround is really ugly. The more preferable way would be to override the code, instead of copying and changing it. Believe me, I tried that. But if you go through the source code in haystack/admin.py file, you'll see overriding is not possible. Well, it is possible, but, it would still be ugly. Just go through the code, you'll see. If you have a better workaround, I'd love to know.