Búsquedas sin acento en Django

IT Igor Támara Igor Támara

Igor Támara

Senior Software Engineer
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()

Referentes


Written by Igor Támara

IT Igor Támara Igor Támara

A seasoned developer, Igor brings expertise in designing and building complex software systems. With a focus on quality and performance, they lead projects that drive innovation and deliver reliable solutions to meet user needs.

Newsletter

Subscribe to our newsletter:

Read more

Build Once. Own Forever.