Accueil

Traduction

Tutoriel Python - sommaire

Tutoriel Python - recherche

L'auteur : Patrick Darcheville

Vous pouvez me contacter via Facebook pour questions & suggestions : Page Facebook relative à mon site

Flask et les formulaires

Dans le cadre d'une application web il faut très souvent recourir aux formulaires afin que l'internaute puisse effectuer des saisies.
La première application web que je vous propose pourrait s'intituler 'espace_perso'. Elle consiste en effet à l'internaute à se connecter à son espace personel

La dernière application est l'occasion de revoir l'importance du mot clé 'global'.

Rappels sur les formulaires

Pour aborder correctement ce chapitre vous devez maitriser la syntaxe HTML relatives aux formulaires. Je vous invite donc à suivre éventuellement le lien ci-dessous :
les formulaires HTML

Application "espace_perso"

Je vous propose de créer une application Flask avec des saisies via un formulaire et un traitement des données dans un autre template.
Un troisième template tient lieu de page d'accueil et permet aussi de naviguer dans l'application.

Créez à la racine de C: un nouveau dossier et nommez le "flask_espace_perso". Puis créez dans ce dossier un sous-dossier "templates" (nom impératif).

Notre application comprendra trois templates et un script.

Le template "connexion.htm"

Ce template est bien sûr enregistré dans le dossier 'templates' du répertoire "c:\flask_forms".

Notez l'attribut FOR pour les étiquettes (éléments LABEL) et l'attribut ID de la zone de texte (élément INPUT) associée.

L'attribut FOR de l'élément LABEL doit être égal à l'attribut ID du INPUT associé.
Ainsi en cliquant sur l'étiquette vous sélectionnez de facto la zone de texte.

Le script : "application.py" (contenu provisoire)

Pour le moment deux routes seulement dans le script :

# script de l'application flask_forms
# nom du script : application.py

from flask import Flask, render_template, request

app = Flask(__name__)

@app.route('/')
def index():
    return render_template ("index.htm")

@app.route('/connexion')
def connexion():
   return render_template("connexion.htm")
    
# ici il y aura une troisième route
   
if __name__ == "__main__":
    app.run(debug = True)

Le template "index.htm"

Cette page s'affichera dès que vous aurez saisi localhost:5000 dans la barre d'url du navigateur.

Donc si vous cliquez sur le dernier lien ('Me connecter') le template "connexion.htm" va s'afficher.

Suite du code de 'application.py'

Nous avons vu que les données saisies dans le formulaire sont envoyées à la route "/traitement" avec la méthode POST :
<form action="/traitement" method ="post">

Ci-dessous le code de cette route qui va traiter les données envoyées par le formulaire :

...
@app.route('/traitement' , methods = ['POST'])
def traitement():
    donnees = request.form
    identifiant = donnees.get('identifiant')
    motpasse = donnees.get('motpasse')
    nom = donnees.get('nom')
    print(identifiant, motpasse, nom) 
	#instruction provisoire 

    # récupération dans la base de données du mot de passe pour identifiant saisi
    identifiant_mem ="dupont@free.fr"
    motpasse_mem ="sesame62"
    
    if identifiant == identifiant_mem and motpasse == motpasse_mem :
        return render_template('traitement.htm', nom_utilisateur = nom)
    else :
        return render_template("traitement.htm")
La méthode form du module request permet de récupérer les données saisies dans un dictionnaire.

Il faut ensuite vérifier si il y a concordance entre identifiant et mot de passe.
Dans la pratique il faudrait alors rechercher dans la base de données le mot de passe correspondant à l'identifiant saisi.
Si il y a concordance entre identifiant et mot de passse alors le nom du visiteur est transmis au template "traitement.htm" sous le vocable "nom_utilisateur". Sinon il y a simplement redirection vers le même template.

Le template "traitement.htm"

L'algorithme de ce code :

 
Si le champ "nom_utilisateur" existe alors : 
	"vous êtes connecté
			affichage de liens pour naviguer dans l'espace perso
Sinon : 
		"Identifiant ou mot de passe incorrect !"
Fsi

Notez comment on écrit un test dans un template ; n'oubliez pas les accolades et les %.
Attention jamais d'espace entre { et % ainsi qu'entre % et } sinon le moteur de template plante.

Testons cette application !

À partir du gestionnaire de fichiers double-cliquez sur le fichier "application.py" dans le répertoire "flask_forms".
Le texte dans le terminal :

 * Serving Flask app 'application'
 * Debug mode: on
WARNING: This is a development server. Do not use it ...
 * Running on http://127.0.0.1:5000
Press CTRL+C to quit
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 139-935-359

Lancez le navigateur et tapez en guise d'URL : localhost:5000 !
Le template "index.htm" s'affiche.

Cliquez sur le lien "Me connecter" !
Le template "connexion.htm" s'affiche.

Renseignez le formulaire et appuyez sur envoi !

Après la soumission le template "traitement.htm" s'affiche.

Ci-dessous le rendu du template "traitement.htm" si concordance entre identifiant et mot de passe

Observez le terminal windows !
Du texte a été rajouté :

...
dupont@free.fr seesame62 Dupont Julien
127.0.0.1 - - [19/Feb/2025 18:50:56] "POST /traitement HTTP/1.1" 200 
...

Les données dupont@free.fr seesame62 Dupont Julien ont bien été transmises de "connexion.htm" vers "application.py" via la méthode POST.

Faites des bêtises !

Je vais vous montrez maitenant comment transférer les données avec la méthode GET. Je vais aussi vous expliquez pourquoi cette méthode n'est pas ici appropriée.

Modifications à effectuer dans "connexion.htm"

On modifie la valeur de l'attribut method dans la balise FORM.
<form ... method ="get">

Modifications à effectuer dans "application.py"

On modifie la route "/traitement".

@app.route(...   , methods = ['GET'])
def traitement():
	donnees = request.args
	identifiant = donnees.get('identifiant')
	...

Retenez : pour récupérer des données envoyées avec la méthode GET il faut utiliser la méthode args du module request.

La barre d'URL

Suite à la connexion la barre d'URL du navigateur affiche :
localhost:5000/traitement?identifiant=dupont%40free.fr&motpasse=sesame62&nom=Dupont+jules
Bravo la confidentialite ... le mot de passe est affiché.
En effet, avec la méthode GET les données soumissionnées sont affichées dans la barre d'URL.

Conclusion

Dès qu'il y a des données sensibles (mots de passe) comme c'est ici, il ne faut surtout pas soumissionner les données avec la méthode GET.

Mais maintenant, et c'est le plus important, vous savez traiter les données avec les deux méthodes.

Application "calculette"

Vous devez réaliser une calculatrice en ligne.
Cette application comprendra un script et deux templates.
Comme les données qui transitent n'ont aucun caractère confidentiel, les soumissions se feront avec la méthode GET.
Créez un nouveau dossier à la racine de C: et nommez le "flask_calculette".

Extrait du script : "calculatrice.py"

...
@app.route('/')
def index():
    return render_template ("calculette.htm")
   
@app.route('/traitement' , methods = ['GET'])
def traitement():
    donnees = request.args
    n1 = donnees.get('n1')
    n2 = donnees.get('n2')
    ope = donnees.get('ope')
    n1=eval(n1)
    n2 =eval(n2)
    print(donnees)
    if ope =="+" :
        res = n1 + n2
    if ope == "-" :
        res = n1 - n2
    if ope == "*" :
        res = n1 * n2
    if ope == "/" :
        res = n1 / n2
    return render_template("resultat.htm", premier = n1, operation =ope, deuxieme = n2 , resultat = res)
...

Je rappelle que pour récupérer les données passées dans l'URL (conséquences de la méthode GET) il faut utiliser la méthode request.args (et non pas request.form).

Extrait de "calculette.htm"

J'utilise des champs de type "number".
Attention pour pouvoir saisir des nombres décimaux dans un INPUT type = number il faut que l'attribut step soit à "any". La valeur par défaut de cet attribut est 1 ; donc si cet attribut est absent on ne peut saisir que des entiers.
Il y a aussi une liste (élément SELECT) qui contient différents items (éléments OPTION).
Rappel : lorsque vous cliquez sur un item c'est la "value" qui est envoyée !

Extrait de "resultat.htm"

À vous de compléter le contenu de l'élément P ; voir le code de la route "/traitement'.

Transmettre des données avec l'URL

Dans le formulaire vous avez saisi 40, + , 40 et appuyé sur "calculer".
Le template "resulat.htm" affiche 40 + 40 donne 80

Observez la barre d'URL, vous lisez : localhost:5000/traitement?n1=40&ope=%2B&n2=40

Modifiez maintenant l'URL :
localhost:5000/traitement?n1=50&ope=%2B&n2=50 puis appuyez sur ENTER
Le template "resultat.htm" affiche 50 + 50 donne 100.

Retenez : soumissionner des données avec la méthode GET équivaut à transmettre des données dans l'URL.

Faites des bêtises

Dans la route "/traitement" tranformez en commentaires les instructions suivantes :

n1=eval(n1)
n2 =eval(n2)

Il suffit de faire précéder chaque instruction de # pour qu'elles ne soient plus interprétées.

Relancez l'application et demandez l'addition de 20 par 30 et vous obtenez 2030 ... (concaténation).

Toute saisie dans un INPUT (même une suite de chiffres) est une chaine !
Donc il faut convertir la chaine numérique en nombre avant toute tentative de calcul.

Prolongements

Le template "resultat.htm" est-il vraiment nécessaire ???
Ne pourrait-on pas imaginer que le résultat du calcul s'affiche dans le template "calculette.htm" à la suite du formulaire ???
Non le template "resultat.htm" n'est pas nécessaire. Les résultats peuvent être renvoyés dans "calculette.htm".
Les modifications à opérer sont minimes.

Modifications dans le template "calculette.htm"

Rajoutez après l'élément FORM quelques lignes :

L'élément H2 sera affiché seulement si la variable resultat existe donc si une soumission a été effectuée.

Modification dans "calculatrice.py"

Et plus précisément la dernière ligne de la fonction traitement() :
return render_template("calculette.htm", premier = n1, ...)
On redirige les résultats du traitement vers le template "calculette.htm".

Le rendu

Pour faire un nouveau calcul vous pouvez renseigner de nouveau le formulaire ou modifier l'URL ...

Application "nombre à trouver"

Vous devez vous souvenir de cette thématique. C'est l'un des premiers programme structuré que vous avez réalisé en Python.

Je rappelle la thématique : un nombre entier compris entre 1 et 99 est généré de façon aléatoire (et est masqué bien sûr).
Le joueur doit deviner ce nombre secret avec le moins d'essais possible (score d'une partie = 10 - nbre d'essais). Donc s'il trouve le nombre secret après 5 essais son score est 10-5 = 5.
Pour sa recherche il est aidé par le message qui apparait après chaque saisie : "L'entier que tu proposes est plus petit que le secret" ou "l'entier que tu proposes est plus grand que le secret".

Rappel du programme évoqué dans le chapitre 20

Ce script ne peut s'exécuter que dans un Shell.

# nom programme : entier_a_trouver.py

def unepartie():
    score = 10
    secret = random.randint(1,100)
    print(secret) # instruction provisoire
    while True:
        propose =""             # entier proposé par le joueur
        try:
            propose =int(input("Propose un nombre  :  "))
            if secret == propose : 
                if score < 0 :
                    score = 0
                print (f"Enfin trouvé ; ton score est  : {score}")
                return score  #la fonction retourne le score de la partie
                break
            elif secret > propose : 
                print(f"{propose}  est trop petit",end="  ")
                score -= 1 
            else:
                print(f"{propose} est trop grand", end="  ")
                score -= 1  
        except:
                print("Erreur de saisie")
# fin fonction

# programme principal
import random
encore ="O"
while encore not in "Nn" :
    score_partie = unepartie() 
    # récupération du score sur 10
    encore = input("Encore jouer O/N ?")
    score_total += score_partie
    compteur +=1
# fin boucle while
print(f"points obtenus : {score_total} sur  {compteur * 10}")

Objectif : faire l'application web correspondante

À la racine de c: créez un nouveau dossier et nommez le "flask_nbre_a_trouver".

Le script "nbre_a_trouver.py"

Ce script est bien sûr enregistré à la racine du nouveau dossier.

Attention le script est un peu délicat car on a des variables initialisées dans une fonction et manipulées dans une autre ... C'est un cas de figure peu fréquent en programmation ...Mais ici je suis obligé de passer d'une route à une autre. Et à chaque route correspond une fonction.

# script de l'application flask_nbre_a_trouver
# nom du script : nbre_a_trouver.py

from flask import Flask, render_template, request
import random

app = Flask(__name__)

@app.route('/')
def index():
    global secret, essais
    secret = random.randint(1,100)
    print('nombre secret : ', secret)
    essais = 0
    return render_template ("nbre_a_trouver.htm")
    
@app.route('/traitement', methods =['GET'])
def traitement():
    donnees = request.args
    print ('entier saisi : ' , donnees)
    saisi = donnees.get('entier')
    saisi = eval(saisi)
    if saisi == secret : 
        compare ="ok"
    elif saisi < secret : 
        compare = "-"
            
    elif saisi > secret :
        compare = "+"
     
    global essais
    essais+=1
    return render_template("nbre_a_trouver.htm", fcompare = compare, fsaisi = saisi, fessais = essais)              
        
if __name__ == "__main__":
    app.run(debug = True)

Étudiez attentivement la fonction index().
Notez l'instruction : global secret, essais
En effet ces deux variables sont définies dans la fonction index() mais doivent être aussi manipulées dans la fonction traitement(). Il faut donc les déclarer "global".
Dans la fonction traitement() il faut incrémenter essais ; pour modifier une variable globale dans une fonction il est nécessaire de la déclarer "global".

Je vous invite à revoir le chapitre 4 sur les fonctions pour davantage d'explications. Les fonctions en Python

L'instruction qui affiche le nombre secret dans le shell est bien sûr provisoire.

Le template "nbre_a_trouver.htm"

Ce template est bien sûr enregistré dans le sous-dossier "templates".

Le rendu

Notez le lien qui s'affiche en cas de découverte du nombre secret.
Ce lien redirige vers la route "/" . Donc un nouvel entier est généré ; le compteur "essais" est réinitialisé.

Peut-on utiliser JavaScript dans un template ?

Un template tourne dans le navigateur or un programme JS ne fonctionne que dans le cadre d'un navigateur. Donc un template peut contenir du code JS !

Reprenez l'application "flask-templates" et effectuez quelques modifications.

Les modifications à effectuer

Dans le script "application.py" rajoutez une route

@app.route("/couleurs")
def couleurs(): 
    return render_template("boite_couleurs.htm")

Dans le template "index.htm" rajouter un lien

Nouveau template : "boite_couleurs.htm"

Des applications HTML5-JS

Suivez le lien ci-dessus ; télécharger le fichier compressé ; extrayez du zip pour récupérer "boite_couleurs.htm" ; copiez le vers "c:/flaskl_templates/templates".

Le contenu du template "boite_couleurs.htm" :

Notez la feuille de style interne ; un formulaire avec plusieurs fois l'attribut "onchange" pour appeler une fonction JS : fcouleur().

Donc sans aucune modification du code, un document HTML complet (HTML,CSS & JS) peut devenir un template de notre application Flask.

Tests

Double-cliquez sur 'application.py' ; le serveur tourne sur le port 5000.
Ouvrez un navigateur et tapez "localhost:5000" dans la barre d'URL ; le template "index.htm" s'affiche.
Cliquez sur le dernier lien ; le template "boite_couleurs.htm" s'affiche.
Déplacez les curseurs pour composer une jolie couleur qui s'affiche dans la boite ; le code RGBA correspondant s'affiche !

La surprise du chef !