I had a similar problem trying to use the pg_tgrm extension to support efficient searches for contains and icontains Django fields.
There might be a more elegant way, but defining a new index type like me worked for me:
from django.contrib.postgres.indexes import GinIndex class TrigramIndex(GinIndex): def get_sql_create_template_values(self, model, schema_editor, using): fields = [model._meta.get_field(field_name) for field_name, order in self.fields_orders] tablespace_sql = schema_editor._get_index_tablespace_sql(model, fields) quote_name = schema_editor.quote_name columns = [ ('%s %s' % (quote_name(field.column), order)).strip() + ' gin_trgm_ops' for field, (field_name, order) in zip(fields, self.fields_orders) ] return { 'table': quote_name(model._meta.db_table), 'name': quote_name(self.name), 'columns': ', '.join(columns), 'using': using, 'extra': tablespace_sql, }
The get_sql_create_template_values method get_sql_create_template_values copied from Index.get_sql_create_template_values() with only one modification: adding + ' gin_trgm_ops' .
For your use case, you then define an index on name_txt using this TrigramIndex instead of GinIndex . Then run makemigrations , which will result in a migration that generates the required CREATE INDEX SQL.
UPDATE:
I see that you are also executing a query using icontains :
result.exclude(name_txt__icontains = 'sp.')
The Postgresql backend will turn this into something like this:
UPPER("NCBI_names"."name_txt"::text) LIKE UPPER('sp.')
and then the trigram index will not be used due to UPPER() .
I had the same problem and ended up subclassing the database server to get around it:
from django.db.backends.postgresql import base, operations class DatabaseFeatures(base.DatabaseFeatures): pass class DatabaseOperations(operations.DatabaseOperations): def lookup_cast(self, lookup_type, internal_type=None): lookup = '%s'