Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Struttura avanzata del progetto

In questo capitolo vedremo come:

  • organizzare un progetto Flask in moduli separati
  • usare l’application factory per creare l’app
  • gestire configurazioni diverse per sviluppo e produzione
  • strutturare Blueprint multipli in modo professionale

Il problema della crescita

Fino ad ora tutto il codice stava in app.py. Per progetti piccoli va bene, ma man mano che l’applicazione cresce — login, upload, API, decine di route — un solo file diventa difficile da leggere e mantenere.

Considera un progetto reale con autenticazione e upload:

app.py   ← 300+ righe, tutto mescolato

L’obiettivo è arrivare a qualcosa di simile:

progetto/
├── app.py          ← solo 10 righe: crea e avvia l'app
├── config.py       ← configurazione
├── auth/           ← modulo autenticazione
│   ├── __init__.py
│   └── routes.py
├── galleria/       ← modulo galleria
│   ├── __init__.py
│   └── routes.py
├── static/
└── templates/
    ├── base.html
    ├── auth/
    │   └── login.html
    └── galleria/
        └── galleria.html

Ogni modulo ha le sue route, i suoi template, la sua logica — e app.py si limita ad assemblarli.

Application Factory

L’application factory è una funzione che crea e configura l’app Flask. Invece di creare l’app a livello di modulo — come abbiamo fatto finora — la creiamo dentro una funzione chiamata create_app:

Prima (approccio semplice):

# app.py
app = Flask(__name__)
app.secret_key = 'chiave'
# ... tutto il resto

Dopo (application factory):

# app.py
from factory import create_app

app = create_app()

if __name__ == '__main__':
    app.run(debug=True)
# factory.py
from flask import Flask

def create_app():
    app = Flask(__name__)
    app.secret_key = 'chiave_segreta'
    # registra Blueprint, estensioni, ecc.
    return app

Il vantaggio principale è che possiamo creare istanze diverse dell’app con configurazioni diverse — ad esempio una per lo sviluppo e una per la produzione.

Configurazione separata

Creiamo un file config.py con classi di configurazione:

# config.py
import os

class Config:
    SECRET_KEY = 'chiave_segreta_di_default'
    UPLOAD_FOLDER = 'uploads'
    MAX_CONTENT_LENGTH = 2 * 1024 * 1024  # 2 MB

class DevConfig(Config):
    DEBUG = True

class ProdConfig(Config):
    DEBUG = False
    SECRET_KEY = os.environ.get('SECRET_KEY', 'cambia-questa-chiave')

os.environ.get('SECRET_KEY', ...) legge la chiave da una variabile d’ambiente — in produzione non vogliamo mai avere segreti nel codice sorgente.

La factory sceglie la configurazione giusta:

# factory.py
from flask import Flask
from config import DevConfig, ProdConfig
import os

def create_app():
    app = Flask(__name__)

    # Sceglie la configurazione in base all'ambiente
    if os.environ.get('FLASK_ENV') == 'production':
        app.config.from_object(ProdConfig)
    else:
        app.config.from_object(DevConfig)

    return app

Esercizi

Esercizio f461

Prendi uno dei progetti dei capitoli precedenti e riscrivilo usando l’application factory. Crea factory.py con la funzione create_app e config.py con almeno due classi di configurazione (DevConfig e ProdConfig). Verifica che l’app funzioni esattamente come prima.


Blueprint multipli

Ogni modulo dell’applicazione diventa un pacchetto Python — una cartella con un file __init__.py. Il Blueprint viene definito nel file routes.py del pacchetto.

Creiamo il modulo auth:

auth/
├── __init__.py   ← vuoto o con importazioni
└── routes.py     ← Blueprint con le route
# auth/routes.py
from flask import Blueprint, render_template, request, redirect, url_for, flash
from flask_login import login_user, logout_user, login_required

auth = Blueprint('auth', __name__, template_folder='templates')

@auth.route('/login', methods=['GET', 'POST'])
def login():
    # ... logica di login
    return render_template('auth/login.html')

@auth.route('/logout')
@login_required
def logout():
    logout_user()
    return redirect(url_for('auth.login'))

Nota url_for('auth.login') — quando si usano Blueprint, il nome della route va prefissato con il nome del Blueprint.

Template per Blueprint

Ogni Blueprint può avere la propria sottocartella di template:

templates/
├── base.html
├── auth/
│   ├── login.html
│   └── registrazione.html
└── galleria/
    └── galleria.html

Nei template, i link tra moduli diversi usano il prefisso del Blueprint:

<a href="{{ url_for('auth.login') }}">Login</a>
<a href="{{ url_for('galleria.index') }}">Galleria</a>

Registrare i Blueprint nella factory

# factory.py
from flask import Flask
from flask_login import LoginManager
from config import DevConfig, ProdConfig
import os

def create_app():
    app = Flask(__name__)

    if os.environ.get('FLASK_ENV') == 'production':
        app.config.from_object(ProdConfig)
    else:
        app.config.from_object(DevConfig)

    # Inizializza Flask-Login
    login_manager = LoginManager(app)
    login_manager.login_view = 'auth.login'

    @login_manager.user_loader
    def carica_utente(id):
        from auth.utils import trova_utente_per_id
        return trova_utente_per_id(id)

    # Registra i Blueprint
    from auth.routes import auth
    from galleria.routes import galleria

    app.register_blueprint(auth, url_prefix='/auth')
    app.register_blueprint(galleria, url_prefix='/')

    return app

Struttura finale completa

progetto/
├── app.py
├── factory.py
├── config.py
├── utenti.json
├── uploads.json
├── uploads/
├── auth/
│   ├── __init__.py
│   ├── routes.py
│   └── utils.py        ← funzioni di utilità (leggi_utenti, trova_utente...)
├── galleria/
│   ├── __init__.py
│   ├── routes.py
│   └── utils.py        ← funzioni di utilità (leggi_registro, salva_registro...)
└── templates/
    ├── base.html
    ├── auth/
    │   ├── login.html
    │   └── registrazione.html
    └── galleria/
        └── galleria.html
# app.py
from factory import create_app

app = create_app()

if __name__ == '__main__':
    app.run()

app.py è ridotto a tre righe. Tutta la logica di configurazione e registrazione è nella factory.

Esercizi

Esercizio f471

Riorganizza il progetto completo (autenticazione + galleria) nella struttura a pacchetti descritta sopra. I moduli auth e galleria devono essere Blueprint separati, ognuno con le proprie route, utility e template.


Esercizio f472

Aggiungi un terzo Blueprint api con un prefisso /api. Per ora basta un endpoint GET /api/immagini che restituisca la lista delle immagini caricate in formato JSON. Registralo nella factory insieme agli altri due.


Esercizio f473 (più impegnativo)

Aggiungi alla factory la gestione delle cartelle necessarie all’avvio: se la cartella uploads/ non esiste, deve essere creata automaticamente. Fai lo stesso per i file JSON (utenti.json, uploads.json) — se non esistono, inizializzali con una lista vuota [].

Tip

aggiungi questa logica in fondo a create_app, prima del return app.