André Carvalho
André Carvalho

Reputation: 171

Problem with Django Tests and Trigram Similarity

I have a Django application that executes a full-text-search on a database. The view that executes this query is my search_view (I'm ommiting some parts for the sake of simplicity). It just retrieve the results of the search on my Post model and send to the template:

def search_view(request):
    posts = m.Post.objects.all()
    query = request.GET.get('q')

    search_query = SearchQuery(query, config='english')

    qs = Post.objects.annotate(
            rank=SearchRank(F('vector_column'), search_query) + TrigramSimilarity('post_title', query)
        ).filter(rank__gte=0.15).order_by('-rank'), 15
    )

    context = {
        results = qs
    }

    return render(request, 'core/search.html', context)

The application is working just fine. The problem is with a test I created. Here is my tests.py:

class SearchViewTests(TestCase):
    def test_search_without_results(self):
        """
        If the user's query did not retrieve anything
        show him a message informing that
        """
        response = self.client.get(reverse('core:search') + '?q=eksjeispowjskdjies')
        self.assertEqual(response.status_code, 200)
        self.assertContains(response.content, "We didn\'t find anything on our database. We\'re sorry")

This test raises an ProgrammingError exception:

django.db.utils.ProgrammingError: function similarity(character varying, unknown) does not exist
LINE 1: ...plainto_tsquery('english'::regconfig, 'eksjeispowjskdjies')) + SIMILARITY...
                                                             ^
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.

I understand very well this exception, 'cause I got it sometimes. The SIMILARITY function in Postgres accepts two arguments, and both need to be of type TEXT. The exception is raising because the second argument (my query term) is of type UNKNOWN, therefore the function won't work and Django raises the exception. And I don't understand why, because the actual search is working! Even in the shell it works perfectly:

In [1]: from django.test import Client

In [2]: c = Client()

In [3]: response = c.get(reverse('core:search') + '?page=1&q=eksjeispowjskdjies')

In [4]: response
Out[4]: <HttpResponse status_code=200, "text/html; charset=utf-8">

Any ideas about why test doesn't work, but the actual execution of the app works and console test works too?

Upvotes: 1

Views: 743

Answers (1)

Jezulas
Jezulas

Reputation: 11

I had the same problem and this how I solved it in my case:

First of all, the problem was that when Django creates the test database that it is going to use for tests it does not actually run all of your migrations. It simply creates the tables based on your models.

This means that migrations that create some extension in your database, like pg_trgm do not run when creating the test database.

One way to overcome this is to use a fixture in your conftest.py file which will make create said extensions before any tests run.

So, in your conftest.py file add the following:

# the following fixture is used to add the pg_trgm extension to the test database
@pytest.fixture(scope="session", autouse=True)
def django_db_setup(django_db_setup, django_db_blocker):
    """Test session DB setup."""
    with django_db_blocker.unblock():
        with connection.cursor() as cursor:
            cursor.execute("CREATE EXTENSION IF NOT EXISTS pg_trgm;")

You can of course replace pg_trgm with any other extension you require.

PS: You must make sure the extension you are trying to use works for the test database you have chosen. In order to change the database used by Django you can change the value of

DATABASES = {'default': env.db('your_database_connection_uri')}

in your application's settings.py.

Upvotes: 1

Related Questions