Vai al contenuto

Ripasso e struttura del progetto

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_for si scrive il nome della funzione ("annuncio"), non l'indirizzo ("/annuncio"). Se sbagliate nome, Flask risponde con un BuildError.

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).