Filtriamo i corsi
Continuiamo ad estendere la nostra vista per listare i corsi per darci la possibilità di filtrare la lista dei corsi ricercando un test nel titolo o nella descrizione di ogni corso.
Per prima cosa estendiamo il template corsi/templates/corsi/corso_list.html
aggiungendo un box di
ricerca:
{% extends "corsi/base.html" %}
{% block content %}
<h2>Corsi</h2>
<form action="" method="GET">
<div>
<input name="q">
<button>Filtra</button>
</div>
</form>
<ul>
{% for corso in object_list %}
<li><a href="{{ corso.get_absolute_url }}">{{ corso }}</a></li>
{% endfor %}
</ul>
{% endblock %}
Il box di ricerca farà una richiesta alla medesima pagina aggiungendo come query string una chiave q con valore il testo della nostra ricerca.
Quindi dobbiamo estendere la vista per supportare questa funzionalità. Andremo a cambiare
CorsoListView
in questo modo:
from django.db.models import Q
...
class CorsoListView(ListView):
queryset = Corso.objects.all()
def get_queryset(self):
qs = super().get_queryset()
query = self.request.GET.get("q")
if query:
return qs.filter(Q(titolo__icontains=query) | Q(descrizione__icontains=query))
else:
return qs
Abbiamo implementato il metodo get_queryset()
che richiede l'aggiunta dell'attributo queryset
.
Dentro al metodo prima ci salviamo l'output dell'implementazione di default nella variabile qs
,
quindi prendiamo il valore della chiave q e se esiste lo usiamo per filtrare ulteriormente il
QuerySet
.
Per filtrare il QuerySet
introduciamo due nuove funzionalità, l'oggetto Q e i lookups dei campi.
Gli oggetti Q ci permettono di implementare le query con filtri in OR logico (vedi |), se avessimo
messo due condizioni all'interno del filter()
o due filter()
di seguito le condizioni sarebbero
state in AND logico.
I lookups invece ci permettono di filtrare nei campi in modo più specifico rispetto alla semplice
uguaglianza, in questo caso abbiamo usato icontains
per cercare all'interno del campo in modo
case-insensitive la presenza di una stringa.
Puntiamo il browser su http://127.0.0.1:8000/corsi/corsi/ e testiamo che funzioni a dovere.
Ora che abbiamo visto che funziona possiamo scrivere un test automatico per verificare che continui
a farlo. Creiamo un file corsi/tests/test_views.py
con il seguente contenuto:
from django.test import TestCase
from django.urls import reverse
from corsi.models import Corso
class CorsoListViewTestCase(TestCase):
def test_filtro_titolo(self):
Corso.objects.create(titolo="titolo corso")
url = reverse("corsi-list")
response = self.client.get(f"{url}?q=tito")
self.assertContains(response, "titolo corso")
def test_filtro_descrizione(self):
Corso.objects.create(titolo="titolo corso", descrizione="descrizione")
url = reverse("corsi-list")
response = self.client.get(f"{url}?q=descr")
self.assertContains(response, "titolo corso")
def test_filtro_senza_match(self):
Corso.objects.create(titolo="titolo", descrizione="descrizione")
url = reverse("corsi-list")
response = self.client.get(f"{url}?q=nomatch")
self.assertNotContains(response, "titolo corso")
In questo TestCase
abbiamo introdotto due nuovi tipi di assert assertContains
e assertNotContains
che rispettivamente controllano che una stringa sia presente o meno nella risposta ad una chiamata.
Per effettuare queste chiamate usiamo il client presente per default come attributo client
all'interno
della classe. Il client viene inizializzato da zero per ogni singolo test.
Facciamo girare i test con il comando:
python3 manage.py test --keepdb
Lo switch --keepdb
non fa cancellare e creare a Django un nuovo database nel quale far girare i test
se ne esiste già uno.
Per concludere aggiorniamo i nostri progressi in git:
git add corsi
git commit -m "Aggiungiamo filtro sulla lista dei corsi"
git push origin main
Esercizi
Replichiamo le due viste, le url, i template ed i test che abbiamo creato per i corsi anche per le categorie. Salva i progressi su git e pubblicali su GitHub.
Guarda come è fatto l'oggetto HttpRequest
di Django nella
documentazione
Guarda quali altri lookups sono disponibili nella documentazione
Approfondisci l'oggetto Q nella documentazione
Leggi la documentazione dei tool di testing e di assertContains