Cesco
Cesco

Il mio blog personale

Francesco
Author

Share


Tags


Usare l'estensione flask-wtf per gestire i form di un sito

FrancescoFrancesco

La prima cosa che devo ricordarmi di fare è di importare la classe form dall'estensione:

from flask_wtf import Form  

Subito dopo devo ricordarmi di aggiungere alla configurazione di Flask il parametro SECRET_KEYcon una stringa di mia fantasia, in questa maniera:

app = Flask(__name__)  
app.config["SECRET_KEY"] = "Stringa inventata di sana pianta"  

Perchè occorre fare questo? Per proteggersi dal Cross-Site Request Forgery (CSRF). Flask-wtf ha già incorporati i sistemi per proteggermi da questa minaccia, in cui un sito web ostile potrebbe inviare dei request ad un'altro sito su cui la vittima è loggata.

Poi mi raccomando: una volta scritto il template HTML/Jinja2 devo aggiungere questo oppure il form non funzionerà correttamente:

{{ form.csrf_token }}

Fatto questo sono pronto a partire.

Quando si utilizza Flask-WTF, ogni form viene rappresentata con una classe

La classe con cui rappresento il form deve essere fatta derivare dalla classe Form che ho importato pocanzi.

Vediamo un'esempio:

from wtforms import Form, StringField, SelectField, SubmitField  
from wtforms.validators import DataRequired

class FormNome(Form):  
    nome = StringField("Come ti chiami ?", validators=[DataRequired()])
    sesso = SelectField("Qual'è il tuo sesso ?", choices=[("m", "Maschio"), ("f", "Femmina")])
    invia = SubmitField("Invia i dati")

La struttura assomiglia a quella che utilizzerebbe un'ORM, non è vero? In questo esempio abbiamo:

Come faccio ad utilizzare questa form/classe nel mio programma adesso ?

Creo un'istanza della classe assicurandomi di passare come parametro il valore di request.form, quindi la passo al metodo render_template, così:

@app.route('/', methods=["POST", "GET"])
def index():  
    form = FormNome(request.form)
    return render_template("index.html", form=form)

All'interno del mio file template in HTML/Jinja2 posso inserire i campi del mio form e le rispettive label piuttosto semplicemente; basta solo che io ricordi i nomi dei campi che ho creato nella classe così:

<html>  
<head>  
...
</head>  
<body>  
<form action="" method="post">  
    <!-- Per prevenire gli attacchi CSRF -->
    <!-- e fare in modo che il form funzioni -->
    {{ form.csrf_token }}
    <!-- Ora seguono i campi veri e propri -->
    {{ form.nome.label }} {{ form.nome | safe }}
    {{ form.sesso.label }} {{ form.sesso | safe }}
    {{ form.invia() }}
</form>  
</body>  
</html>  

Il risultato sarà visivamente circa questo:

Immagine del risultato della form

Quando l'utente invia il form, come faccio a gestire i dati che ci sono scritti dentro ?

Intanto mi devo assicurare che i dati siano stati inviati tramite un metodo POST: per farlo avrò bisogno di consultare il valore di request.method.
Poi dovrò utilizzare il metodo validate() che è già presente nella mia form per vedere se il form è stato compilato e validato (tutti i campi hanno i valori che erano stati richiesti).
Così:

@app.route('/', methods=["POST", "GET"])
def hello_world():  
    form = FormNome(request.form)
    if request.method == "POST" and form.validate():
        # Pronto a gestire i dati del form

I dati inviati nel form sono disponibili all'interno della variabile data. Ad esempio per accedere al campo nome dovrò scrivere form.nome.data; mentre per accedere al sesso selezionato dovrò scrivere form.sesso.data.
Ecco un'altro esempio che completa il precedente con la raccolta dati:

@app.route('/', methods=["POST", "GET"])
def hello_world():  
    form = FormNome(request.form)
    if request.method == "POST" and form.validate():
        nome = form.nome.data
        sesso = form.sesso.data

Ecco infine il codice completo che gestisce tutto il form e restituisce una stringa formattata di saluti:

from flask import Flask, render_template, request  
from wtforms import Form, StringField, SelectField, SubmitField  
from wtforms.validators import DataRequired


class FormNome(Form):  
    nome = StringField("Come ti chiami ?", validators=[DataRequired()])
    sesso = SelectField("Qual'è il tuo sesso ?", choices=[("m", "Maschio"), ("f", "Femmina")])
    invia = SubmitField("Invia i dati")


app = Flask(__name__)  
app.config["SECRET_KEY"] = "Cinciallegra sbarabaus passava di qui"


@app.route('/', methods=["POST", "GET"])
def hello_world():  
    form = FormNome(request.form)
    output = "..."
    if request.method == "POST" and form.validate():
        output = "Ciao " + form.nome.data + ", "
        if form.sesso.data == "m":
            output += "vedo che sei un bel maschietto"
        elif form.sesso.data == "f":
            output += "vedo che sei una bella femminuccia"
        else:
            output += "non ho la minima idea di quale sia il tuo sesso"

    return render_template("index.html", form=form, output=output)

Ed ecco il codice del template in HTML/Jinja2 che visualizza il form e la risposta:

<!DOCTYPE html>  
<html lang="en">  
<head>  
    <meta charset="UTF-8">
    <title>Titolo</title>
</head>  
<style>  
    label, #invia {
        margin-left: 1em;
    }
    label:first-child {
        margin-left: 0;
    }
    #risposta {
        font-weight: bold;
        margin-top: 1.5em;
    }
</style>  
<body>  
<h1>Questa è la mia form</h1>

<form action="" method="post">  
    {{ form.nome.label }} {{ form.nome | safe }}
    {{ form.sesso.label }} {{ form.sesso | safe }}
    {{ form.invia(id="invia") }}
</form>  
<br/>  
<div id="risposta">  
    {{ output }}
</div>  
</body>  
</html>  
Francesco
Author

Francesco

Comments