Ricerca testuale su Django Admin usando Search Fields

Tue 24 May 2022

La ricerca testuale è indispensabile per poter filtrare i dati in maniera efficiente, soprattutto quando il numero di informazioni, contenute nel nostro applicativo, cresce.

Django Admin offre la possibilità di aggiungere, all'interno del backoffice, i campi di ricerca in maniera intuitiva ed elegante. Con poche istruzioni, lo sviluppatore può mettere a disposizione dei propri utenti dei campi di ricerca che eseguono diverse tipologie di filtri: parola esatta, parole contenute, etc.

In questo articolo capirai come inserire i campi di ricerca all'interno di Django Admin utilizzando il campo search_fields. Una volta inseriti i campi di ricerca, il backoffice di Django ci mostra una pagina che si presenta come segue:

Backoffice Django Admin con ricerca testuale

Abilitazione della ricerca testuale sull'elenco degli elementi del modello

Per abilitare la ricerca testuale, all'interno della pagina contenente l'elenco degli elementi di un modello, inserire nella classe del ModelAdmin del modello, la seguente istruzione:

search_fields = ['colonna1', 'colonna2', ]

Nella precedente lista, colonna1 e colonna2 rappresentano i nomi dei campi sui quali eseguiremo la ricerca testuale.

Ricerca su campi testuali CharField e TextField

Un primo tipo di ricerca testuale può essere eseguito sui campi del modello di tipo testuale: CharField e TextField. Per eseguire una ricerca di questo tipo, inserire i nomi dei campi all'interno della variabile search_fields.

Inserendo i nomi di colonne senza specificare nulla, il filtro testuale lavora in modalità icontains, ossia eseguendo una like case insensitive.

Ad esempio, se inserisci una chiave di ricerca mario e stai ricercando sul campo nome, la condizione eseguita sul database è:

where nome ilike '%mario%'

Ricerca su campi relazionati tramite ForeignKey e ManyToManyField

Puoi eseguire ricerche sui campi relazionati mediante ForeignKey e ManyToManyField. In particolare, puoi specificare il campo, della tabella relazionata, sul quale eseguire la ricerca testuale.

Supponi di avere i seguenti modelli:

class Autore(models.Model):
    nome = models.CharField(max_length=128)
    cognome = models.CharField(max_length=128)

class Album(models.Model)
    nome = models.CharField(max_length=128)
    autore = models.ForeignKey(Autore, on_delete=models.PROTECT)

Potrai creare una ricerca testuale, sulla lista degli elementi di Album, sul campo nome del modello Autore, come segue:

class AlbumAdmin(admin.ModelAdmin):
    search_fields = ['autore__nome']

La precedente notazione, contenente una lookup verso il campo nome, consente di ricercare testualmente, tramite icontains, all'interno del campo nome del modello autore.

Funzionamento della ricerca testuale tramite search_fields

Dopo aver visto le modalità con le quali è possibile inserire una ricerca all'interno dei ModelAdmin, vediamo come opera la ricerca attivata dall'istruzione search_fields.

Supponi di avere il modello Autore, descritto in precedenza, e di aver definito le seguenti ricerche:

class AutoreAdmin(admin.ModelAdmin):
    search_fields = ['nome', 'cognome']

Se inserisci la chiave di ricerca Tiziano Terzani, la ricerca produce la seguente condizione where su SQL:

where 
    (nome ILIKE '%Tiziano%' OR cognome ILIKE '%Tiziano%') 
    AND 
    (nome ILIKE '%Terzani%' OR cognome ILIKE '%Terzani%')

Puoi notare che ogni chiave inserita è ricercata all'interno dei campi, e deve essere presente in almeno un campo affinchè sia restituito l'elemento corrispondente.

Definizione lookup di ricerca

Dall'esempio del paragrafo precedente, si deduce che le ricerche testuali sono eseguite mediante icontains per default.

Django Admin consente tuttavia di utilizzare le seguenti lookup di ricerca:

  • startswith
  • iexact o exact
  • search

startswith

La lookup startswith consente di specificare il testo da ricercare all'inizio del testo che si sta considerando. Una ricerca di questo tipo produce una clausula where come segue:

where nome ILIKE 'Pippo%'

iexact o exact

La lookup iexact o exact consente di eseguire una ricerca puntuale della chiave di ricerca inserita. Tale ricerca impone delle uguaglianze nella clausola where; è consigliabile utilizzarla solamente per ricerche che prevedono una chiave singola, poiché chiavi di ricerca multiple restituiranno sempre risultati nulli a meno che non si inseriscano chiavi che soddisfino esattamente ogni singolo campo di ricerca sul quale è imposta la clausula exact.

search

La lookup search esegue ricerche fulltext su campi con tipo TextField.

Le lookup di ricerca possono essere applicati sia a campi diretti del modello considerato, sia a campi relazionati dal modello considerato. La notazione per questi ultimi campi sarà del tipo modello__campo__lookup.

Personalizzazione ricerca testuale con ModelAdmin.get_search_results()

Django Admin consente la personalizzazione delle ricerche testuali mediante la definizione nel ModelAdmin del metodo get_search_results.

Il metodo get_search_results accetta in ingresso come parametri, la request, il queryset ottenuto dai filtri fino alla chiamata del metodo, e le chiavi di ricerca search_term fornite dall'utente.

Il metodo restituisce una tupla contenente il queryset filtrato e un flag booleano che indica se i risultati contengono duplicati o meno.

L'implementazione di default prevede la ricerca all'interno dei campi definiti da search_fields.

Le ricerche eseguibili vanno dalla ricerca per numero interno alla ricerca su strumenti esterni quali Solr o Haystack.

Se ad esempio volessi imporre la ricerca intera sul campo anno_nascita del modello Persona, scriverai il seguente codice:

class PersonaAdmin(admin.ModelAdmin):
    search_fields = ['nome']

    def get_search_results(self, request, queryset, search_term):
        queryset, use_distinct = \
            super().get_search_results(request, queryset, search_term)
        try:
            search_term_as_int = int(search_term)
        except ValueError:
            pass
        else:
            queryset |= self.model.objects.filter(
                anno_nascita=search_term_as_int
            )
        return queryset, use_distinct

Da notare che la specifica della ricerca per il campo anno_nascita avviene all'interno del metodo e non del search_fields.

Conclusioni e Riferimenti

Gli esempi mostrati consentono di creare ricerche testuali all'interno del backoffice di Django Admin.

Il video mostra il tuturial dell'articolo che stai leggendo: