Aggiungere filtri su Django Admin con List Filter

Tue 17 May 2022

Creare filtri nelle maschere di backoffice è indispensabile per poter comodamente fruire i dati per verificare, ad esempio, le registrazioni degli utenti, le vendite di prodotti, o la prossima attività da fare. Tuttavia visualizzare un intero elenco di prodotti, seppur paginati ed ordinabili nelle colonne dei dati, molte volte non consente di avere una visione di insieme precisa e puntuale. Ad esempio, qualora si vogliono vedere i soli prodotti venduti in una certa data, non conviene estrarre le informazioni da un elenco che prevede dati anche di date differenti da quella considerata. Per aiutare la navigazione dei dati ci vengono in soccorso i filtri. Un filtro consente di scremare i dati presentati e visualizzare solo quelli necessari.

Il framework Django, attraverso Django Admin, consente di aggiungere filtri alle proprie pagine in modo molto semplice. Attraverso i filtri è possibile visualizzare, ad esempio, tutti i prodotti in un certo stato, tutti i contatti che vivono a Rovigo, e così via. In Django Admin è possibile aggiungere filtri utilizzando un list_filter. Un list_filter aggiunge, sulla sidebar della pagina contenente la lista degli elementi di un determinato modello, i filtri attivati. La pagina si presenta come segue:

Backoffice Django Admin con filtri

Sulla parte destra della pagina si nota una colonna contenente i filtri aggiunti con list_filter. In questo articolo vedrai come aggiungere tali filtri.

Come definire un list_filter

Un list_filter è aggiunto, come tupla o lista di elementi, all'interno del ModelAdmin di un Model.

La definizione come tupla è:

from django.contrib import admin

class ProdottoAdmin(admin.ModelAdmin)
    list_filter = ()

La definizione come lista è:

from django.contrib import admin

class ProdottoAdmin(admin.ModelAdmin)
    list_filter = []

Ogni elemento deve appartenere ad una delle seguenti categorie:

  • Filtro sul nome dei campi del Model
  • Filtro come classe che estende da SimpleListFilter
  • Filtro su tipi predefiniti (vedremo l'esempio del BooleanFieldListFilter)
  • Filtro su nome dei campi del tipo RelatedOnlyFieldListFilter

Nel seguito dell'articolo andremo a specificare i tipi indicati nell'elenco.

Elemento list_filter definito sul nome dei campi del Model

L'elemento definisce il nome del campo del Model al quale associare il filtro. Il filtro mostra tutti i valori, non duplicati, presenti all'interno del campo considerato.

Qualora il filtro risulti applicato su una ForeignKey, esso mostra tutti gli elementi presenti sul modello collegato, ed anche quelli che non hanno associazioni con elementi del modello sul quale il filtro è associato. Questo comportamento si differenzia dal comportamento del filtro RelatedOnlyFieldListFilter, che mostra solamente le tuple che hanno tuple associate sulla tabella sulla quale il filtro è applicato.

L'elemento può applicare il filtro ai seguenti campi del Model:

  • BooleanField
  • CharField
  • DateField
  • DateTimeField
  • IntegerField
  • ForeignKey
  • ManyToManyField

Un esempio di definizione è la seguente:

from django.contrib import admin

class ProdottoAdmin(admin.ModelAdmin)
    list_filter = ('status', 'marca', )

I nomi presenti nella dichiarazione list_filter possono contenere campi di altri modelli collegati al modello tramite relazione. Per utilizzare campi specifici di altri modelli si usa la notazione __.

from django.contrib import admin

class ProdottoAdmin(admin.ModelAdmin)
    list_filter = ('marca__nome', )

Elemento list_filter definito come classe che estende da SimpleListFilter

Un elemento del filtro che estende dalla classe SimpleListFilter consente di creare filtri personalizzati di elevata complessità. Filtri di questo tipo prevedono la gestione della query da eseguire sui dati per filtrare le tuple sul backoffice.

La definizione del filtro all'interno del ModelAdmin è la seguente:

list_display = [MioFiltroListFilter]

La definizione della classe prevede l'estensione dalla classe admin.SimpleListFilter come segue:

from django.contrib import admin

class MioFiltroListFilter(admin.SimpleListFilter):
    title = ''
    parameter_name = ''

def lookups(self, request, model_admin):
    pass

def queryset(self, request, queryset):
    pass

Il blocco di codice precedente evidenzia la segnatura necessaria per creare un filtro di questo tipo. La classe filtro creata deve avere un proprio title che rappresenta il nome da dare al filtro (il nome comparirà all'interno del backoffice). La classe dovrà anche definire il parameter_name ossia il nome del parametro con il quale il filtro invierà i parametri alla query_string. La classe prevede l'override del metodo lookups che prevede la restituzione di una tupla contenente n elementi che saranno le voci che compariranno nel filtro. Ogni elemento della tupla sarà costituito da:

(valore_query_string, nome_filtro)

Dove valore_query_string è il valore passato dal filtro nella query_string al momento della richiesta; mentre nome_filtro è il nome associato al filtro che compare sul backoffice.

Un esempio di metodo lookups completo è il seguente:

def lookups(self, request, model_admin):
    return (
        ("1", "dietetico"),
        ("2", "normale"),
        ("3", "calorico"),
    )

Infine, l'override del metodo queryset implementa le query per filtrare i dati del modello. All'interno del metodo possiamo recuperare il parametro passato in query_string utilizzando la chiamata self.value(). Se non inseriamo alcun tipo di logica nel metodo, saranno restituiti tutti i dati ad ogni chiamata del filtro. Un esempio di override è il seguente:

def queryset(self, request, queryset):
    if self.value() == "1":
        return queryset.filter(calorie__lt=100)
    if self.value() == "2":
        return queryset.filter(calorie__gte=100,calorie__lt=200)
    if self.value() == "3":
        return queryset.filter(calorie__gte=200)

Elemento list_filter su tipi predefiniti

Un elemento del list_filter di questo tipo opera su tipi predefiniti. Ad esempio se vogliamo creare un filtro per un campo del modello di tipi BooleanField possiamo definire il seguente elemento:

list_filter = (
    ('is_vegetale', admin.BooleanFieldListFilter),
)

dove is_vegetale è il nome del campo a cui si fa riferimento.

Elemento list_filter sul nome dei campi del tipo RelatedOnlyFieldListFilter

Tale elemento si differenzia rispetto al filtro sul nome del campo presentato in precedenza poiché, in questo caso, il filtro mostra solo le voci del modello che hanno un riferimento con il modello sul quale il filtro è applicato.

Ad esempio, supponiamo che il mio modello Tipologia abbia i seguenti elementi:

  • suv
  • utilitaria
  • sportiva

e che solamente suv ed utilitaria abbiano associazioni con il modello Auto. Se applicassi sul ModelAdmin di Auto il filtro:

list_filter = (
    ('tipologia', admin.RelatedOnlyFieldListFilter),
)

otterrei nel filtro solamente le voci suv e utilitaria poiché sono le uniche che hanno una corrispondenza con auto.

Conclusioni e Riferimenti

Gli esempi mostrati nell'articolo consentono di creare filtri avanzati all'interno del backoffice di Django Admin.

Il seguente video mostra ciò che è spiegato nell'articolo.