Insights
Búsquedas sin acento en Django
Igor Támara
1 min read.
Buscar Bogotá o Bogota debería ofrecer el mismo resultado, los humanos no son tan estrictos, así que hay varias opciones para hacer que Django con Postgresql pueda encontrar cadenas sin importar si las personas escriben o no con acentos. En esta entrada veremos cómo configurar postgresql y Django para lograr este efecto. Personalmente creo que si hay que escribir con acentos, porque es distinto "bajo la escalera" a "bajó la escalera".
Nuestra aproximación empleará la extensión unaccent
de postgresql, aunque postgresql desde la versión 9.1 ofrece collation para contar con índices en los idiomas que se requiera, Django no tiene incorporada la opción de collation para Postgresql. Este artículo es válido hasta Django 1.6, de Django 1.7 en adelante ver al final.
Configurar postgresql
El primer paso es contar con la extensión
unaccent
, para la base de datos destino se puede emplear
psql -c "CREATE EXTENSION unaccent;" destino
La instalación de la extensión requiere que en un sistema Linux como Debian tenga el paquete contrib, por ejemplo, para postgresql 9.3:
sudo apt-get install postgresql-contrib-9.3
Mokey Patch de Django
En un archivo que se invoque la mínima cantidad de veces se puede colocar el siguiente bloque de código
from django.db.backends.postgresql_psycopg2.base import DatabaseOperations
from django.db.backends.postgresql_psycopg2.base import DatabaseWrapper
def lookup_cast(self, lookup_type):
if lookup_type in('icontains', 'istartswith', 'iexact'):
return "UPPER(unaccent(%s::text))"
elif lookup_type in('contains', 'startswith'):
return "unaccent(%s::text)"
else:
return super(DatabaseOperations, self).lookup_cast(lookup_type)
def patch_unaccent():
DatabaseOperations.lookup_cast = lookup_cast
DatabaseWrapper.operators['icontains'] = 'LIKE UPPER(unaccent(%s))'
DatabaseWrapper.operators['istartswith'] = 'LIKE UPPER(unaccent(%s))'
DatabaseWrapper.operators['iexact'] = '= UPPER(unaccent(%s))'
DatabaseWrapper.operators['contains'] = 'LIKE unaccent(%s)'
DatabaseWrapper.operators['startswith'] = 'LIKE unaccent(%s)'
patch_unaccent()