Lookup fields customitzats en Django

Published on junio 3rd, 2018

Django, de manera genèrica, ens proveeix una API del seu ORM a través de la qual ens permet executar els "querysets" que necessitem de manera més o menys optima. Malgrat això, es pot donar el cas que necessitem realitzar algun tipus de consulta una mica més customitzada o millorada. Per a tal efecte, disposem de tota una sèrie d'eines, ja siguin els objectes Q, les expressions F, els Field Lookups, etc. Sobre aquest darrer grup, els Field lookup's tenim la possibilitat de definir els nostres propis.

Per a implementar un "Lookup field" hem d'implementar una classe que ens permeti personalitzar la part esquerra i la part dreta de la clàusula sql. Es fa estenent la classe Lookup del paquet "django.db.models.fields.Lookup" i, posteriorment, registrant aquest classe personalitzada amb el mètode register_lookup de la classe django.db.models.fields.Field. E.g.:

from django.db.models import Lookup
from django.db.models.fields import Field


class Lower(Lookup):
    """
    Custom lookup for postgres "lower" function implementation
    """
    lookup_name = 'lower'

    def as_sql(self, qn, connection):
        lhs, lhs_params = self.process_lhs(qn, connection)
        rhs, rhs_params = self.process_rhs(qn, connection)
        params = lhs_params + rhs_params
        return 'lower({}) = {}'.format(lhs, rhs), params

Field.register_lookup(Lower)

L'exemple en concret implementa la funcionalitat de comparar el contingut d'un camp en minúscula amb la cadena que nosaltres li passem. Hi ha varis punts importants:

lookup_name = 'lower'

Defineix el nom que emprarem per executar la clàusula.

lhs, lhs_params = self.process_lhs(qn, connection)

Compila la part esquerra de la clàusula

rhs, rhs_params = self.process_rhs(qn, connection)

Compila la part dreta de la clàusula

Field.register_lookup(Lower)

Registrem el custom lookup per tal de poder-lo emprar.

Finalment, un exemple d'ús del nostre custom lookup seria:

try:
    task = TransTask.objects.filter(object_class__lower=class_name).get()
except TransTask.DoesNotExist:
    task = None

 

object_class__lower=class_name

és la part on s'aplica el "custom lookup". Compararà el contingut en minúscula del camp

object_class

amb el contingut de la variable class_name.