Testing django admin search_fields

Tags: django

In the django admin you can add search fields. Very handy! You get a search field at the top of your admin page to quickly filter the results.

Here’s an example:

class InvitationAdmin(admin.ModelAdmin):
    model = models.Invitation
    list_display = ['email', 'name', 'is_activated', 'activated_on']
    search_fields = ['name', 'email']

See the django documentation.

There is a mistake I often make: adding a foreignkey field in the search_fields list! Let’s take the standard django book/author example. You have a Book and an Author model. Book has a foreignkey to Author.

On the admin page that lists the books you want to have a search box. Not only for book titles, but you also want to search on authors’ names and get their books. Easy:

class BookAdmin(admin.ModelAdmin):
    model = models.Book
    list_display = ['title', 'isbn', 'num_pages', 'author']
    search_fields = ['title, 'author']

Let’s try it out in the admin. Oops:

Traceback (most recent call last):
...
TypeError: Related Field got invalid lookup: icontains

You have to point at a proper texually-searchable field instead of directly to a model (via a ForeignKey field). It is easy to fix:

class BookAdmin(admin.ModelAdmin):
    ...
    search_fields = ['title, 'author__name']

I’ve written a very simple test that tests all my ModelAdmin’s search_fields:

from django.test import TestCase
from myapp import admin


class TestSearchFields(TestCase):

    def test_for_valid_search_fields(self):
        # It is easy to add a foreignkey in a search field instead of a
        # stringfield on the class the foreign key points to.
        for model_admin_class in [
                # Hardcoded list
                admin.PortalAdmin,
                admin.TokenAdmin,
                admin.InvitationAdmin,
                admin.UserProfileAdmin,
                admin.RoleAdmin,
                admin.OrganisationAdmin,
                admin.OrganisationRoleAdmin]:
            model_class = model_admin_class.model
            print("Testing search fields for %s" % model_class)
            for fieldname in model_admin_class.search_fields:
                query = '%s__icontains' % fieldname
                print("Testing with %s" % query)
                kwargs = {query: 'reinout'}
                # We have no content, so the number of results if we search on
                # something should be zero. The only thing that matters is
                # that we get no 'cannot search on foreignkey' error.
                self.assertEquals(
                    model_class.objects.filter(**kwargs).count(),
                    0)

Works like a charm! It immediately reported an error in one of my search_fields :-)

Note: I rely on print() to tell me what went wrong. Which works, as I use nose as a test runner, which suppresses the output unless the test fails.

 
vanrees.org logo

About me

My name is Reinout van Rees and I work a lot with Python (programming language) and Django (website framework). I live in The Netherlands and I'm happily married to Annie van Rees-Kooiman.

Weblog feeds

Most of my website content is in my weblog. You can keep up to date by subscribing to the automatic feeds (for instance with Google reader):