In questa lezione impari a
- creare un ambiente virtuale per il progetto
- riconoscere le cartelle di un'applicazione Flask e a cosa servono
- usare route con parametri tipizzati, url_for e redirect
- gestire gli errori con abort ed errorhandler
In quarta avete visto che Flask fa al posto vostro tutto il lavoro "artigianale" dei socket: apre la porta, interpreta le richieste HTTP, gestisce più client insieme. Ripartiamo da lì, organizzando il progetto come si fa nel mondo reale.
Il kit della lezione
Avviatelo (vedi la lezione 0) e provate questi indirizzi:
| Indirizzo | Cosa succede |
|---|---|
/ |
l'elenco degli annunci |
/annuncio/2 |
un annuncio |
/annuncio/99 |
la pagina "non trovata" personalizzata |
/annuncio/ciao |
ancora "non trovata": perché? (lo scoprite tra poco) |
/vecchia-bacheca |
vi riporta alla home: guardate l'indirizzo nella barra! |
L'ambiente virtuale
Ogni progetto Python dovrebbe avere il suo ambiente virtuale: una cartella .venv con una copia isolata di Python e delle librerie che usa.
Perché? Immaginate due progetti: uno vecchio che funziona solo con Flask 2, uno nuovo con Flask 3. Se le librerie sono installate "per tutto il PC" non possono convivere. Con un ambiente per progetto, sì.
# dentro la cartella del kit, una volta sola:
python -m venv .venv
# attivazione (ogni volta che aprite un nuovo terminale)
.venv\Scripts\activate # Windows
source .venv/bin/activate # Linux / macOS
pip install -r requirements.txt
Quando l'ambiente è attivo, il terminale mostra (.venv) all'inizio della riga. VS Code di solito se ne accorge da solo e vi chiede se usarlo: rispondete sì.
Se su Windows l'attivazione dà un errore di "esecuzione di script disabilitata", nel terminale di VS Code scegliete il terminale Command Prompt invece di PowerShell (freccetta accanto al +).
La cartella .venv non si consegna e non va su GitHub: chi riceve il progetto se la ricrea con requirements.txt.
Le cartelle del progetto
flask5-01-struttura/
├── app.py ← l'applicazione: dati, funzioni e route
├── requirements.txt ← le librerie da installare
├── templates/ ← le pagine HTML (Jinja2)
│ ├── base.html ← la "cornice" comune a tutte le pagine
│ ├── index.html ← l'elenco degli annunci
│ ├── annuncio.html ← un singolo annuncio
│ └── 404.html ← la pagina di errore
└── static/ ← i file che il server manda così come sono
└── css/style.css
Flask cerca i template in templates/ e i file statici in static/: i nomi vanno scritti esattamente così. I template li studiamo nella prossima lezione; oggi ci concentriamo su app.py.
Dentro app.py
Il file è diviso in tre parti, sempre nello stesso ordine: i dati, le funzioni di aiuto, le route.
1. I dati
annunci = [
{"id": 1, "titolo": "Verifica di TPSIT", "categoria": "compito",
"testo": "Martedì verifica pratica su Flask...", "data": "2026-11-10"},
...
]
Una lista di dizionari: ogni dizionario è un annuncio. Per ora i dati stanno in memoria: se riavviate il server, ogni modifica si perde. Nella lezione 4 li sposteremo in un database.
2. Una funzione di aiuto
def trova_annuncio(id):
for a in annunci:
if a["id"] == id:
return a
return None
Cerca l'annuncio con quell'id; se non c'è restituisce None ("niente"). La teniamo separata dalle route perché la useremo in più punti.
3. Le route
@app.route("/annuncio/<int:id>")
def annuncio(id):
a = trova_annuncio(id)
if a is None:
abort(404)
return render_template("annuncio.html", annuncio=a)
Seguiamo una richiesta a /annuncio/2:
GET /annuncio/2
│
▼
Flask cerca una route che corrisponde → "/annuncio/<int:id>"
│ il convertitore int trasforma "2" nel numero 2
▼
annuncio(id=2)
│ trova_annuncio(2) → il dizionario dell'uscita didattica
▼
render_template("annuncio.html", annuncio=...) → HTML
│
▼
risposta 200 OK con la pagina
I convertitori
<int:id> fa due cose: converte il testo dell'indirizzo in un numero e rifiuta gli indirizzi non validi. Ecco perché /annuncio/ciao dà 404: la route non corrisponde nemmeno, la funzione non viene chiamata.
| Convertitore | Accetta | Esempio |
|---|---|---|
<nome> |
testo senza / (predefinito) |
/categoria/evento |
<int:id> |
numeri interi | /annuncio/7 |
<float:prezzo> |
numeri decimali | /sconto/9.5 |
<path:percorso> |
testo che può contenere / |
/file/doc/a.pdf |
url_for: niente indirizzi scritti a mano
Invece di scrivere "/annuncio/7", si chiede a Flask di costruire l'indirizzo a partire dal nome della funzione:
url_for("home") # "/"
url_for("annuncio", id=7) # "/annuncio/7"
url_for("home", pagina=2) # "/?pagina=2" (i parametri in più vanno dopo il ?)
Se un giorno cambiate @app.route("/annuncio/<int:id>") in @app.route("/avviso/<int:id>"), tutti i link costruiti con url_for si aggiornano da soli.
Attenzione: in
url_forsi scrive il nome della funzione ("annuncio"), non l'indirizzo ("/annuncio"). Se sbagliate nome, Flask risponde con unBuildError.
redirect: mandare l'utente altrove
@app.route("/vecchia-bacheca")
def vecchia_bacheca():
return redirect(url_for("home"))
Il server non manda una pagina, ma il codice 302 con l'indirizzo nuovo; il browser fa da solo una seconda richiesta. Lo userete moltissimo: dopo il salvataggio di un form, dopo il login, dopo il logout.
Gestire gli errori
if a is None:
abort(404) # interrompe la funzione: "non trovato"
@app.errorhandler(404)
def non_trovata(errore):
return render_template("404.html"), 404
abort(404)interrompe subito la richiesta con quel codice di errore.@app.errorhandler(404)decide cosa mostrare al posto della pagina grezza di Flask.- Il secondo valore,
404, è il codice di stato: senza, Flask risponderebbe 200 (tutto bene) anche per una pagina di errore, e i motori di ricerca la indicizzerebbero come pagina vera.
| Codice | Significato |
|---|---|
| 200 | OK |
| 302 | redirect, vai altrove |
| 404 | non trovato |
| 500 | errore nel server (un'eccezione Python) |
Missioni
⭐ Missione 1 – Un annuncio in più
Aggiungete alla lista un quarto annuncio (con id 4 e una categoria tra avviso, compito, evento). Ricaricate: compare da solo nell'elenco e la sua pagina funziona.
⭐ Missione 2 – La pagina info
Aggiungete la route /info che restituisce (anche come semplice testo) il nome della bacheca, la classe e quanti annunci ci sono.
Aiutino
Il numero di annunci è len(annunci). Restituite una f-string.
⭐⭐ Missione 3 – Un annuncio a caso
Aggiungete /casuale, che fa redirect alla pagina di un annuncio scelto a caso.
Aiutino
import random in cima al file; random.choice(annunci) sceglie un elemento della lista. Poi redirect(url_for("annuncio", id=...)).
⭐⭐ Missione 4 – Il codice giusto
Aprite gli strumenti per sviluppatori del browser (F12), scheda Rete, e visitate /, /annuncio/1, /annuncio/99 e /vecchia-bacheca. Annotate il codice di stato di ogni richiesta. Poi togliete , 404 dall'errorhandler: cosa cambia nella scheda Rete? E nella pagina?
⭐⭐⭐ Missione 5 – Avanti e indietro
Aggiungete /annuncio/<int:id>/successivo: fa redirect all'annuncio con l'id più piccolo tra quelli maggiori di id. Se non ce n'è nessuno, torna alla home. Poi aggiungete in annuncio.html un link "Annuncio successivo →".
Aiutino
maggiori = [a["id"] for a in annunci if a["id"] > id]. Se la lista non è vuota, min(maggiori) è quello che cercate. Nel template: url_for('successivo', id=annuncio.id) (se la funzione si chiama successivo).