Il dettaglio del singolo corso
Dopo aver fatto la vista per listare i nostri corsi creiamo una vista per vedere il dettaglio di ogni
singolo corso. Aggiungiamo la nuova vista in corsi/views.py
.
from django.views.generic import DetailView, ListView
from corsi.models import Corso
class CorsoListView(ListView):
model = Corso
class CorsoDetailView(DetailView):
context_object_name = 'corso'
queryset = Corso.objects.all()
Questa nuova vista CorsoDetailView
estende la vista generica DetailView
e sovrascrive due attributi:
context_object_name
per usarecorso
come nome di variabile contente l'istanza diCorso
passata al template, altrimenti sarebbe stataobject
.queryset
per specificare il QuerySet dal quale prendere l'istanza, utile per filtrare a monte i modelli che vogliamo poter richiamare da questa vista.
L'uso di questi attributi è abbastanza arbitrario, avremmo potuto definire model
come abbiamo fatto
per CorsoListView
ma avremmo visto due attributi utili in meno.
Fatta la nostra vista dobbiamo aggiungerla in corsi/urls.py
aggiungendola in urlpatterns
:
urlpatterns = [
path("corsi/", views.CorsoListView.as_view(), name="corsi-list"),
path("corsi/<int:pk>/", views.CorsoDetailView.as_view(), name="corsi-detail"),
]
Qui possiamo fare attenzione a due cose: la prima è che i path sono considerati chiusi, nel senso che una vista risponderà ad un path solo se viene trovata una corrispondenza esatta; se non fosse stato così il secondo path non sarebbe stato raggiungibile.
L'altra cosa da notare è la cattura dei parametri nell'url, la sintassi è formata da due parti separate
da :
:
- la prima indica il tipo, in questo esempio
int
per catturare solo le cifre; non è obbligatoria e se non fornita cattura il contenuto fino al prossimo/
. - La seconda parte è il nome che diamo alla variabile passata alla vista,
pk
è dettata daDetailView
, in questo caso la variabile è configurabile tramite l'attributopk_url_kwarg
. Le viste fatte a classi appaiono come veramente magiche quando le si usano, bisogna farci la mano.
Collegata la vista al routing possiamo creare il template che si aspetta
corsi/templates/corsi/corso_detail.html
:
{% extends "corsi/base.html" %}
{% block content %}
<h2>Corso: {{ corso }}</h2>
<p>Categoria: {{ corso.categoria }}</p>
<p>Docenti: {% for docente in corso.docenti.all %}{{ docente.username }}{% endfor %}</p>
<p>Descrizione: {{ corso.descrizione }}</p>
{% endblock %}
Dovrebbe essere quasi tutto familiare, facciamo attenzione solo alla variabile che usiamo per il for
,
essenzialmente è lo stesso codice che avremmo scritto in una vista o nella shell ma senza le parentesi.
Abbiamo tutti i pezzi per poter chiamare la nostra vista dal browser, il fatto che dobbiamo sapere
l'id del nostro modello però è un po' scomodo. Per ovviare a questo possiamo estendere il nostro
modello Corso
, aggiungendo un metodo get_absolute_url
che tramite la funzione reverse
recupera
il path assoluto della vista per il singolo corso.
Usare reverse
ci permette di poter cambiare in futuro i path delle nostre url senza dover aggiornarlo
in tante occorrenze sparse nel nostro codice.
from django.db import models
from django.contrib.auth.models import User
from django.urls import reverse
class Categoria(models.Model):
titolo = models.CharField(max_length=100)
creato = models.DateTimeField(auto_now_add=True)
aggiornato = models.DateTimeField(auto_now=True)
def __str__(self):
return self.titolo
class Meta:
verbose_name_plural = "Categorie"
class Corso(models.Model):
titolo = models.CharField(max_length=100)
descrizione = models.TextField()
categoria = models.ForeignKey(Categoria, null=True, blank=True, on_delete=models.PROTECT)
docenti = models.ManyToManyField(User)
creato = models.DateTimeField(auto_now_add=True)
aggiornato = models.DateTimeField(auto_now=True)
def __str__(self):
return self.titolo
def get_absolute_url(self):
return reverse("corsi-detail", args=[self.pk])
class Meta:
verbose_name_plural = "Corsi"
Possiamo aggiornare il template corsi/templates/corsi/corso_list.html
per trasformare le occorrenze
dei corsi in un link alla relativa pagina di dettaglio:
{% extends "corsi/base.html" %}
{% block content %}
<h2>Corsi</h2>
<ul>
{% for corso in object_list %}
<li><a href="{{ corso.get_absolute_url }}">{{ corso }}</a></li>
{% endfor %}
</ul>
{% endblock %}
Puntiamo il browser all'indirizzo http://127.0.0.1:8000/corsi/corsi/ per verificare che funzioni.
Infine ricordiamoci sempre di aggiornare il nostro codice su git:
git add corsi
git commit -m "Aggiungiamo vista per dettaglio corso"
git push origin main
Esercizi
Leggi la documentazione di DetailView.
Leggi la documentazione delle urls.
Leggi la documentazione di get_absolute_url.
Leggi la documentazione di reverse