U6-S2-M5Développement web côté serveur : Python et HTML
0 / 0
Scénario 2 · TechnoVert

Développement web côté serveur : Python et HTML

Tout ce dont vous avez besoin pour ce module, dans un seul document. Cochez au fur et à mesure.

U6-S2-M5module
Élèvedocument
TechnoVertscénario 2
D · 5 h 30format et durée

Comment utiliser ce document

  • À gauche, une partie à la fois. Cliquez pour changer de partie.
  • Les boutons bleus A1 ouvrent une annexe dans le panneau de droite.
  • Vos réponses et vos cases cochées sont gardées dans ce navigateur.
Rappels

Ce qu'il faut savoir avant de commencer

Ce qu'il faut savoir avant de commencer. Lisez-le si vous avez un doute. Environ 10 minutes.

C'est quoi une fonction en Python ?

Une fonction est un bloc de code qui porte un nom. On l'écrit une fois, on l'appelle quand on veut.

Elle reçoit des valeurs entre parenthèses et renvoie un résultat avec return.

def double(nombre):
    return nombre * 2

double(4)      # renvoie 8

C'est quoi un dictionnaire ?

Un dictionnaire range des valeurs sous des noms, appelés clés. On lit une valeur avec sa clé entre crochets.

chantier = {"id": 3, "nom": "Éclairage des allées", "statut": "ouvert"}
chantier["nom"]        # "Éclairage des allées"
"statut" in chantier   # True : la clé existe

Une liste de dictionnaires, c'est un tableau : une ligne par dictionnaire.

Comment lire un message d'erreur Python ?

Quand un programme plante, Python affiche une trace. On la lit par le bas.

La dernière ligne donne le type d'erreur et sa cause. Juste au-dessus, on trouve le fichier et le numéro de la ligne fautive.

  File "calcul.py", line 4, in moyenne
    return total / nombre
ZeroDivisionError: division by zero

Ici, la ligne 4 de calcul.py divise par zéro : la variable nombre vaut 0.

Comment joindre un programme sur le réseau ?

Une machine a une adresse IP. Sur cette machine, chaque programme serveur écoute sur un numéro de port.

Dans le navigateur, on écrit l'adresse puis le port après deux-points : http://127.0.0.1:5000. L'adresse 127.0.0.1 désigne votre propre poste. Le port 5000 est celui qu'on utilise pendant le développement.

Quelques outils Python du labo

Le labo vous fait lire et compléter du code. Voici les mots qui reviennent.

Écrit dans le code Ce que ça veut dire
if …: puis elif …: « si » puis « sinon si » : on teste un autre cas.
x is None vrai si x ne contient aucune valeur.
"a" not in liste vrai si "a" n'est pas dans la liste.
len(x) le nombre de caractères d'un texte, ou d'éléments d'une liste.
x.strip() le texte x sans les espaces au début et à la fin.
"5".isdigit() vrai si le texte ne contient que des chiffres.
liste.append(valeur) ajoute valeur à la fin de la liste.
try: … except ValueError: … « essaie ceci ; si ça rate avec cette erreur, fais cela ».

Vérifiez-vous

Voir les réponses
  1. def bonjour(nom): puis, en retrait, return "Bonjour " + nom.
  2. Le texte "ouvert".
  3. Le code utilise une variable chantier qui n'a jamais reçu de valeur. Il faut chercher la ligne indiquée juste au-dessus.
Cours

Ce que fait le serveur, ce que fait le navigateur

COURS-U6-S2-05

Karim Benali dirige l'atelier de TechnoVert. Chaque soir, ses techniciens rentrent de chantier. Ils ont cinq minutes pour noter leur intervention, pas plus.

Aujourd'hui, ils la notent dans un tableur partagé. Un jour, un technicien a saisi sur un chantier déjà terminé. Personne ne l'a vu pendant trois semaines.

Karim veut une page web : on se connecte, on remplit, on envoie. Et si la saisie est fausse, la page doit refuser.

À la fin de la séance, vous saurez comment un serveur Python construit cette page, reçoit la saisie et la vérifie.

Le plan de la séance

  1. Qui fait quoi : le serveur ou le navigateur.
  2. Route, vue, gabarit : le chemin d'une page.
  3. Recevoir une saisie et la vérifier.
  4. Se souvenir de l'utilisateur : la session.
  5. Quand le serveur plante.

La question de départ

Vous remplissez un formulaire sur un site, puis vous cliquez sur « Envoyer ». Qui doit vérifier que ce que vous avez tapé est juste : votre navigateur ou le serveur ?

Capsule 1 sur 5Qui fait quoi : le serveur ou le navigateur

On part de ce que vous faites tous les jours : taper une adresse et voir une page. Mais qui fabrique cette page ?

Le navigateur envoie une REQUÊTE au serveur. C'est un court message qui dit : « donne-moi la page /chantiers ». Le serveur répond avec une page écrite en HTML, le langage qui décrit le contenu d'une page : titres, tableaux, formulaires.

Pensez à un fast-food. Vous passez commande au comptoir, la cuisine prépare, on vous tend le plateau. Vous ne voyez jamais la cuisine.

Le navigateur, c'est vous. Le serveur, c'est la cuisine.

  1. Le navigateur envoie une requête : une adresse, parfois des données.
  2. Le serveur exécute du code Python : il lit les données, fait les calculs.
  3. Le serveur renvoie du HTML : une page toute prête.
  4. Le navigateur affiche la page. Il applique le CSS, le fichier qui règle les couleurs, les tailles et les marges.
Navigateur chez l'utilisateur affiche le HTML applique le CSS ne décide rien Serveur dans l'entreprise exécute le Python lit les données vérifie la saisie 1. requête : GET /chantiers 2. réponse : une page HTML Le code Python ne quitte jamais le serveur.
Le navigateur demande, le serveur construit la page en Python et renvoie du HTML

Le HTML contient aussi le formulaire : les champs à remplir et le bouton « Envoyer ». Le navigateur l'affiche. Mais c'est le serveur qui reçoit et traite ce qu'on y tape.

On le fait ensemble — la page de saisie de Karim

1. Qui envoie la requête quand un technicien ouvre la page de saisie ?

2. Où se trouve la liste des chantiers affichée dans le menu déroulant ?

3. Qui choisit la couleur verte du bandeau ?

4. Le technicien clique sur « Enregistrer ». Qui range l'intervention ?

Exemple Pourquoi
✔ Le serveur lit le fichier des chantiers et met la liste dans la page. Les données restent sur le serveur. Seul le résultat part vers le navigateur.
✘ « Le navigateur va chercher les chantiers dans le fichier. » Le navigateur n'a pas accès aux fichiers du serveur. Il reçoit seulement du HTML.
✘ « Le CSS vérifie que la durée est un nombre. » Le CSS règle l'apparence. Il ne vérifie rien et ne calcule rien.

Checkpoint

À l'oral, 3 minutes. Chacun note sa réponse sur sa feuille, puis on corrige.

1. Vous faites clic droit, puis « Afficher le code source » sur une page. Voyez-vous le code Python du serveur ?

2. Karim veut des lettres plus grandes sur la page. Faut-il changer le Python, le HTML ou le CSS ?

Capsule 2 sur 5Route, vue, gabarit : le chemin d'une page

Le serveur construit donc la page. Reste à savoir comment le code Python sait quelle page construire.

On utilise un cadriciel web, une boîte à outils qui gère les requêtes pour nous. Ici, c'est Flask, en Python. Avec Flask, une page passe par trois pièces : la ROUTE, la VUE et le GABARIT.

C'est comme au lycée. L'emploi du temps dit « mardi 10 h, salle B12 » : c'est la route.

Le professeur fait le cours : c'est la vue. Le manuel donne la mise en page : c'est le gabarit.

  1. La route relie une adresse à une fonction : /chantiers mène à liste_chantiers().
  2. La vue est cette fonction Python. Elle va chercher les données.
  3. La vue passe les données au gabarit, sous un nom : chantiers=....
  4. Le gabarit est un fichier HTML avec des trous. Il remplit les trous avec les données. Flask utilise pour ça Jinja2.
@app.route("/chantiers")                        # la route
def liste_chantiers():                          # la vue
    chantiers = donnees.lire_chantiers()        # les données
    return render_template("chantiers.html", chantiers=chantiers)

Dans le gabarit, {{ c.nom }} affiche une valeur. {% for c in chantiers %} répète une ligne pour chaque chantier.

Une adresse → une route → une vue → un gabarit rempli → du HTML Adresse /chantiers Route @app.route Vue liste_chantiers() Gabarit chantiers.html HTML donnees.lire_chantiers() les 6 chantiers reçoit chantiers=… remplit {{ c.nom }} Le nom passé par la vue est le nom lu par le gabarit.
De l'adresse /chantiers à la page affichée : route, vue, données, gabarit

On le fait ensemble — la page /saisir

1. Quelle ligne relie l'adresse /saisir à une fonction ?

2. Le gabarit saisir.html contient {% for c in chantiers %}. Quel nom la vue doit-elle utiliser pour passer la liste ?

3. La vue écrit return render_template("saisir.html", liste=chantiers). Que voit-on ?

Exemple Pourquoi
✔ render_template("chantiers.html", chantiers=chantiers) Le nom chantiers est celui que le gabarit attend.
✘ La vue fabrique le HTML avec des + et des print. C'est illisible et fragile. Le HTML va dans le gabarit, le Python dans la vue.
✘ Deux fonctions sous la même route. Flask n'appelle que la première. Une adresse, une vue.

Checkpoint

À l'oral, 3 minutes. Chacun note sa réponse sur sa feuille, puis on corrige.

1. Le navigateur demande /connexion. Quelle pièce Flask regarde-t-il en premier ?

2. Un gabarit affiche {{ message }}. Que doit écrire la vue pour afficher « Enregistré » ?

Capsule 3 sur 5Recevoir une saisie et la vérifier

La vue sait maintenant envoyer des données à la page. Il faut faire le chemin inverse : recevoir ce que le technicien a tapé.

Les données voyagent de deux façons. Avec GET, elles sont dans l'adresse, après un point d'interrogation. Avec POST, elles sont dans le corps de la requête, la partie invisible du message.

C'est comme une carte postale et une lettre. Sur la carte postale, tout le monde lit le texte. Dans l'enveloppe, le texte ne se voit pas de l'extérieur.

Attention : l'enveloppe cache, mais elle ne protège pas. Seul le chiffrement protège.

  1. Choisir la méthode : GET pour chercher ou filtrer, POST pour envoyer une saisie.
  2. Lire les données dans la vue : request.args pour GET, request.form pour POST.
  3. Vérifier chaque champ : présent, du bon type, dans les bonnes limites.
  4. Répondre : s'il y a une erreur, réafficher le formulaire avec un message. Sinon, enregistrer.
GET  /chantiers?service=atelier        → request.args["service"]  vaut "atelier"
POST /saisir   (corps : duree=2.5 ...) → request.form["duree"]    vaut "2.5"

Tout ce qui arrive est du texte. "2.5" n'est pas encore un nombre : la vue doit le convertir, et refuser s'il n'y arrive pas.

GET : les données sont dans l'adresse GET /chantiers ?service=atelier → la vue lit request.args Visible dans la barre d'adresse, l'historique et le journal du serveur. POST : les données sont dans le corps POST /saisir chantier=3 · duree=2.5 · description=… → la vue lit request.form Dans les deux cas, tout arrive en texte : la vue vérifie chaque champ.
Deux chemins pour les données : dans l'adresse avec GET, dans le corps avec POST

On le fait ensemble — une saisie de durée

1. Le technicien envoie le formulaire de saisie. GET ou POST ?

2. Où la vue lit-elle la durée ?

3. Le champ contient deux heures. Que fait la vue ?

4. Le champ contient 40. Est-ce accepté ?

Exemple Pourquoi
✔ /chantiers?service=atelier pour filtrer la liste Un filtre ne modifie rien. L'adresse peut être gardée en favori.
✘ Un mot de passe envoyé en GET Il apparaît dans l'adresse, dans l'historique et dans les journaux du serveur.
✘ « Le champ a l'attribut required : pas besoin de vérifier sur le serveur. » Cet attribut est dans le navigateur. On peut l'enlever en deux clics. Le serveur vérifie toujours.

Checkpoint

À l'oral, 3 minutes. Chacun note sa réponse sur sa feuille, puis on corrige.

1. L'adresse /chantiers?service=commercial est demandée. Où la vue lit-elle commercial ?

2. Un chantier a un statut : ouvert ou cloture. On ne saisit plus rien sur un chantier clôturé. Le champ « chantier » contient le numéro d'un chantier clôturé. La saisie est-elle acceptée ?

Capsule 4 sur 5Se souvenir de l'utilisateur : la session

La saisie est vérifiée. Mais Karim veut une chose de plus : seuls les salariés connectés peuvent saisir. Et le serveur oublie tout entre deux requêtes.

Pour s'en souvenir, il y a la SESSION. C'est une petite mémoire liée à un visiteur.

Flask la range dans un cookie, un petit fichier que le navigateur renvoie à chaque requête. Le cookie est signé par une clé secrète du serveur : si quelqu'un le modifie, le serveur le refuse.

C'est le bracelet d'un festival. On le reçoit à l'entrée en montrant son billet. Ensuite, on le montre à chaque scène, sans ressortir le billet.

  1. À la connexion, la vue vérifie l'identifiant et le mot de passe.
  2. Si c'est bon, elle écrit dans la session : session["login"] = login.
  3. Sur une page protégée, la vue regarde la session en premier. Pas de login ? Elle renvoie vers la page de connexion.
  4. À la déconnexion, la vue vide la session : session.clear().

Renvoyer vers une autre page s'appelle une REDIRECTION. Le serveur ne donne pas de page : il répond « va voir à cette adresse ». Chaque réponse porte un code de réponse :

Code Sens Exemple dans le mini-site
200 Tout va bien, voici la page. La liste des chantiers s'affiche.
302 Redirection : va voir ailleurs. Pas connecté : renvoyé vers /connexion.
404 Cette adresse n'existe pas. /chantiers/99 quand il n'y a pas de chantier 99.
500 Le code du serveur a planté. Une erreur Python dans une vue.
Navigateur Serveur GET /saisir, sans cookie 302 : va voir /connexion POST /connexion : login et mot de passe vérifie le compte session["login"] 302 + cookie de session signé garde le cookie GET /saisir + cookie 200 : la page de saisie Chaque page protégée lit la session avant de répondre.
La connexion écrit le login dans la session ; chaque page protégée le vérifie avant de répondre

On le fait ensemble — Yann Ropars ouvre /saisir sans être connecté

1. Que regarde la vue en premier ?

2. Il n'y en a pas. Quel code le serveur renvoie-t-il ?

3. Yann se connecte avec le bon mot de passe. Qu'écrit la vue ?

4. Il rouvre /saisir. Que se passe-t-il ?

Exemple Pourquoi
✔ Après une connexion réussie, une redirection vers la liste L'utilisateur arrive sur une vraie page, et un rechargement ne renvoie pas le mot de passe.
✘ Protéger la page en cachant le lien dans le menu L'adresse reste joignable. Il faut vérifier la session dans la vue.
✘ Mettre le mot de passe dans la session Inutile et risqué. Le login suffit : le mot de passe a déjà été vérifié.

Checkpoint

À l'oral, 3 minutes. Chacun note sa réponse sur sa feuille, puis on corrige.

1. Où Flask range-t-il la session ?

2. Karim clique sur « Se déconnecter », puis rouvre l'adresse /saisir à la main. Peut-il saisir ?

Capsule 5 sur 5Quand le serveur plante

Une vue plante : une clé manque, un calcul échoue. Le serveur répond alors avec le code 500. Mais que voit l'utilisateur ?

Tout dépend du mode du serveur. En mode développement, Flask affiche la trace complète dans le navigateur : le code, les fichiers, les variables. C'est pratique pour le développeur.

En mode production, le navigateur reçoit seulement « Internal Server Error ». La trace part dans le journal du serveur, là où le technicien la lit.

Mode Ce que voit le navigateur Où lire la trace
Développement : flask run --debug La trace complète, le code source Dans la page et dans le terminal
Production : flask run sans --debug « Internal Server Error », rien d'autre Dans le terminal du serveur, ou son journal

La règle : la trace ne doit jamais atteindre le navigateur d'un utilisateur. Elle montre le code, les chemins des fichiers, parfois des mots de passe. Et le mode développement de Flask peut même laisser exécuter du code Python depuis la page.

Instant histoire — 2015 : Patreon et le mode développement

En 2015, Patreon est un site qui aide des artistes à se faire financer par leur public.

Une version de développement du site est restée joignable depuis Internet, avec le mode de débogage allumé.

Des pirates s'en servent pour entrer. Les données de près de 2,3 millions de comptes sont volées puis publiées.

Qu'est-ce qui aurait évité ça ?

Ce qu'on retient

Si vous ne devez retenir que trois choses

1. Le serveur construit la page en Python et envoie du HTML. Le navigateur affiche, il ne décide rien.

2. Chaque saisie se vérifie dans la vue, sur le serveur, champ par champ. Même si le navigateur a déjà vérifié.

3. Une page protégée vérifie la session en premier. Et en production, la trace d'erreur reste dans le journal.

Revenons à Karim. Son technicien ouvre /saisir : sans session, il est renvoyé vers la connexion.

Connecté, il choisit un chantier et envoie le formulaire en POST. La vue voit que le chantier est clôturé, et elle refuse avec un message. L'erreur du tableur ne peut plus passer.

Fiche

Ce que fait le serveur, ce que fait le navigateur

COURS-U6-S2-05

L'essentiel

Les mots à connaître

Mot En clair
Requête Le message du navigateur au serveur : une adresse, parfois des données.
HTML Le langage qui décrit le contenu d'une page.
CSS Le fichier qui règle couleurs, tailles et marges.
Route Le lien entre une adresse et une fonction Python.
Vue La fonction Python qui prépare la réponse.
Gabarit Un fichier HTML avec des trous {{ … }}, rempli par Jinja2.
Session Ce que le serveur retient d'un visiteur entre deux requêtes.
Redirection Réponse 302 : « va voir à cette adresse ».

La méthode : écrire une vue qui reçoit un formulaire

  1. Vérifiez la session. Pas de login : redirection vers /connexion.
  2. Distinguez GET et POST avec request.method.
  3. Lisez chaque champ dans request.form.
  4. Vérifiez chaque champ : présent, bon type, bonnes limites. Notez chaque erreur dans une liste.
  5. Répondez : erreurs → réafficher avec les messages. Sinon → enregistrer, puis afficher la confirmation.

Un exemple concret

Un technicien envoie une durée de abc sur le chantier 3.

Étape Ce qu'on fait Résultat
1 "login" in session Oui : on continue.
2 request.method "POST" : c'est un envoi.
3 request.form["duree"] Le texte "abc".
4 float("abc") Échec : on ajoute « La durée doit être un nombre ».
5 Réafficher saisir.html avec erreurs Le message s'affiche. Rien n'est enregistré.

Conclusion : la saisie fausse est refusée par le serveur, avec un message clair.

Les pièges

Labo

Un mini-site à trois pages

LABO-U6-S2-05

Contexte — Vous travaillez pour la société qui reprend l'informatique de TechnoVert. Karim Benali, le responsable d'atelier, veut que ses techniciens saisissent leurs interventions sur une page web, et plus sur un tableur.

Un collègue a préparé le squelette du site : les adresses, les pages HTML et la feuille de style. Il reste à écrire le code Python qui fait tourner chaque page.

Règle IA de la séance

Scénario 2 : l'IA peut vous expliquer une notion ou un message d'erreur. Elle n'écrit pas votre code.

Si vous lui posez une question, dites-la au formateur au point de contrôle suivant.

À la fin de ce labo, vous saurez écrire les vues d'un site Flask. Elles affichent une liste, traitent un formulaire et protègent une page. Vous saurez aussi lire une erreur serveur.

Comment ça se valide — à chaque point de contrôle, vous montrez votre résultat au formateur. Il valide, puis vous passez à la suite.

Les mots de la séance

Mot Ce que ça veut dire
Route La ligne @app.route(...) qui relie une adresse à une fonction.
Vue La fonction Python placée sous la route. C'est elle que vous écrivez.
Gabarit Le fichier HTML avec des trous {{ … }}, dans le dossier templates. Il est fourni.
GET, POST Deux façons d'envoyer des données : dans l'adresse, ou dans le corps de la requête.
Session Ce que le serveur retient d'un visiteur, comme son identifiant de connexion.
Trace Le message que Python affiche quand il plante. On le lit par le bas.

Ce dont vous avez besoin

En binôme — le pilote tape, le lecteur lit les consignes et les annexes à voix haute, et vérifie chaque résultat. On inverse à la partie C.

Le plan du labo

Partie Étapes Ce que vous obtenez Temps
A — Afficher la liste 1 à 6 la page des chantiers remplie par le serveur 25 min
B — Recevoir un formulaire 7 à 12 une saisie enregistrée par le serveur 30 min
C — Vérifier et protéger 13 à 18 des saisies vérifiées, des pages réservées aux salariés connectés 40 min
D — Lire une erreur serveur 19 à 22 une erreur lue au bon endroit, et le mode production 20 min

PARTIE A — Afficher la liste

Étapes 1 à 6, environ 25 min. À la fin, la page /chantiers affiche les six chantiers de TechnoVert.

Besoin d'un rappel ? Revoir le cours : « Route, vue, gabarit : le chemin d'une page ».

01

Installer le mini-site sur votre poste

Vous devez voir : un dossier mini-site dans « Documents ». Il contient app.py, chantiers.json, donnees.py, et les dossiers static et templates.

02

Ouvrir le dossier dans VS Code

VS Code sert à la fois à modifier le code et à lancer le serveur.

ls

Vous devez voir : la liste des fichiers du dossier, dont app.py et templates.

Si vous voyez une liste qui ne contient pas app.py : vous avez ouvert le mauvais dossier. Ouvrez celui qui contient directement app.py.

03

Lancer le serveur en mode développement

python -m flask --app app run --debug

Les options de cette commande sont expliquées dans l'annexe .

Vous devez voir : entre autres ces lignes.

 * Running on http://127.0.0.1:5000
 * Debugger is active!

Si vous voyez « python n'est pas reconnu » : tapez py à la place de python.

Vous devez voir : le texte « Page des chantiers : à écrire ». La route existe, mais sa vue n'est pas écrite.

Ce terminal reste ouvert jusqu'à la partie D. En mode développement, le serveur recharge seul le code à chaque enregistrement.

04

Lire ce que le gabarit attend

Un gabarit a des trous. La vue doit lui donner les bonnes données, sous le bon nom.

Vous devez avoir : un nom de liste, et cinq clés. Vous les retrouverez dans le code de l'étape 5.

05

Écrire la vue de la liste

Voici la vue complète. Elle lit les chantiers, puis les passe au gabarit sous le nom attendu.

    chantiers = donnees.lire_chantiers()
    return render_template("chantiers.html", chantiers=chantiers)

Vous devez voir : le titre « Les chantiers », la phrase « 6 chantier(s). » et un tableau de six lignes. La première est « Arrosage du parc de la mairie ».

Si vous voyez « IndentationError » dans la page : une ligne n'a pas ses quatre espaces devant. Rouvrez le fichier et alignez-la.

06

Vérifier que la page vient du serveur

Vous devez voir : aucun résultat. Le code source contient seulement du HTML, avec une balise <tr> par chantier.

Pourquoi ? Pourquoi le navigateur ne reçoit-il jamais le code Python de la vue ?

Votre réponse :

POINT DE CONTRÔLE A

  • Montrez au formateur la liste des six chantiers, et votre réponse à l'étape 6.
  • Le formateur a validé. Passez à la partie B.

PARTIE B — Recevoir un formulaire

Étapes 7 à 12, environ 30 min. À la fin, une intervention saisie dans la page est enregistrée par le serveur.

Besoin d'un rappel ? Revoir le cours : « Recevoir une saisie et la vérifier ».

07

Afficher le formulaire vide

La page /saisir reçoit deux sortes de requêtes. En GET, on affiche le formulaire vide. En POST, on reçoit la saisie.

    chantiers = donnees.lire_chantiers()
    if request.method == "GET":
        return render_template("saisir.html", chantiers=chantiers, services=donnees.SERVICES,
                               erreurs=[], saisie={}, message=None)

Vous devez voir : le formulaire, avec six chantiers dans le premier menu et trois services dans le second.

08

Trouver dans la doc

Il faut maintenant lire ce que le technicien a tapé. La méthode est dans la documentation.

Vous devez avoir : deux noms qui commencent par request.. Le premier sert à l'étape 9.

09

Enregistrer la saisie

Pour l'instant, il n'y a pas de connexion. Le technicien s'appelle donc « test ». La partie C remplacera ce nom par le vrai.

    saisie = request.form
    numero = donnees.ajouter_intervention(int(saisie["chantier"]), "test", saisie["service"],
                                          saisie["date"], float(saisie["duree"]),
                                          saisie["description"], saisie["blocage"])
    return render_template("saisir.html", chantiers=chantiers, services=donnees.SERVICES,
                           erreurs=[], saisie={}, message=f"Intervention n° {numero} enregistrée.")

Vous devez voir : la page /saisir s'affiche toujours. Aucune trace d'erreur dans le premier terminal.

10

Envoyer une saisie juste

Vous devez voir : le message vert « Intervention n° 13 enregistrée. ».

Select-String "Contrôle des bornes" chantiers.json

Vous devez voir : une ligne qui contient "description": "Contrôle des bornes", précédée du nom du fichier et du numéro de la ligne.

11

Voir la différence entre GET et POST

127.0.0.1 - - [29/Sep/2026 14:02:11] "POST /saisir HTTP/1.1" 200 -
127.0.0.1 - - [29/Sep/2026 14:03:40] "GET /chantiers?service=atelier HTTP/1.1" 200 -

Vous devez voir : la saisie n'apparaît nulle part dans l'adresse ni dans le journal. Le mot atelier, lui, apparaît dans les deux.

Pourquoi ? Un collègue propose d'envoyer le mot de passe de connexion en GET. Pourquoi est-ce une mauvaise idée ?

Votre réponse :

12

Envoyer une saisie fausse

Vous devez voir : une page d'erreur de Flask. Tout en bas, la ligne ValueError: could not convert string to float: 'deux heures'.

Pourquoi ? Le serveur a planté au lieu d'afficher un message. Qu'est-ce qui manque dans la vue ?

Votre réponse :

POINT DE CONTRÔLE B

  • Montrez au formateur la ligne trouvée à l'étape 10, et vos réponses aux étapes 11 et 12.
  • Le formateur a validé. Passez à la partie C.

PARTIE C — Vérifier et protéger

Étapes 13 à 18, environ 40 min. À la fin, les saisies fausses sont refusées avec un message, et seuls les salariés connectés peuvent saisir.

Besoin d'un rappel ? Revoir le cours : « Se souvenir de l'utilisateur : la session ».

Changez de rôle : le lecteur devient pilote.

13

Vérifier la durée et le chantier

On vérifie chaque champ avant d'enregistrer. Chaque erreur trouvée va dans une liste. Voici les deux premières vérifications, écrites en entier.

    saisie = request.form
    erreurs = []

    chantier = None
    if saisie["chantier"].isdigit():
        chantier = donnees.lire_chantier(int(saisie["chantier"]))
    if chantier is None:
        erreurs.append("Choisissez un chantier.")
    elif chantier["statut"] == "cloture":
        erreurs.append("Ce chantier est clôturé : saisie refusée.")

    duree = 0
    try:
        duree = float(saisie["duree"])
        if duree <= 0 or duree > 12:
            erreurs.append("La durée doit être entre 0 et 12 heures.")
    except ValueError:
        erreurs.append("La durée doit être un nombre, par exemple 2.5.")

    # étape 14 : vos trois vérifications ici

    if erreurs:
        return render_template("saisir.html", chantiers=chantiers, services=donnees.SERVICES,
                               erreurs=erreurs, saisie=saisie, message=None)

    numero = donnees.ajouter_intervention(chantier["id"], "test", saisie["service"],
                                          saisie["date"], duree,
                                          saisie["description"], saisie["blocage"])
    return render_template("saisir.html", chantiers=chantiers, services=donnees.SERVICES,
                           erreurs=[], saisie={}, message=f"Intervention n° {numero} enregistrée.")

Vous devez voir : le formulaire revient, avec le message rouge « La durée doit être un nombre, par exemple 2.5. ». Les champs déjà remplis sont gardés.

14

Écrire les trois autres vérifications

Chaque vérification tient en deux lignes : un if, puis erreurs.append(...). Voici la première, écrite en entier. Elle refuse un service qui n'est pas dans la liste.

    if saisie["service"] not in donnees.SERVICES:
        erreurs.append("Choisissez un service dans la liste.")
Champ Le test à écrire Message à afficher
date le texte ne fait pas 10 caractères : len(saisie["date"]) != 10 La date s'écrit AAAA-MM-JJ.
description le texte sans espaces est vide : saisie["description"].strip() == "" Décrivez ce qui a été fait.

Vous devez voir : la page /saisir s'affiche toujours, sans trace d'erreur.

15

Tester les vérifications

Saisie Résultat attendu Ce que vous obtenez
chantier 1, atelier, 2026-09-29, 3, « Contrôle » refusée : chantier clôturé
chantier 4, aucun service, 2026-09-29, 3, « Contrôle » refusée : service
chantier 4, atelier, 29/09, 3, « Contrôle » refusée : date
chantier 4, atelier, 2026-09-29, 40, description vide refusée : deux messages
chantier 4, atelier, 2026-09-29, 3, « Contrôle des mâts » acceptée

Vous devez voir : exactement le résultat attendu sur chaque ligne. La dernière affiche un numéro d'intervention.

Pourquoi ? Le menu déroulant ne propose que trois services. Pourquoi vérifier quand même le service sur le serveur ?

Votre réponse :

16

Écrire la connexion

La connexion vérifie le compte, puis écrit le login dans la session. La fonction verifier_compte() est fournie : l'annexe la décrit.

    if request.method == "POST":
        login = request.form["login"]
        if donnees.verifier_compte(login, request.form["mot_de_passe"]):
            session["login"] = login
            return redirect(url_for("liste_chantiers"))
        return render_template("connexion.html", erreur="Identifiant ou mot de passe incorrect.")
    return render_template("connexion.html", erreur=None)
    session.clear()
    return redirect(url_for("connexion"))

Vous devez voir : vous arrivez sur la liste des chantiers. Le bandeau affiche « Connecté : » suivi de votre identifiant.

17

Protéger les deux pages

Une page protégée regarde la session avant tout le reste.

    if "login" not in session:
        return redirect(url_for("connexion"))

Vous devez voir : tant que vous êtes connecté, les deux pages s'affichent comme avant.

18

Tester la session

Essai Résultat attendu Code relevé
Cliquez sur « Se déconnecter », puis ouvrez /saisir renvoyé vers la connexion
Connectez-vous avec un mauvais mot de passe message rouge, pas de connexion
Connectez-vous avec le bon mot de passe arrivée sur la liste
Ouvrez /saisir le formulaire s'affiche

Vous devez voir : un cookie nommé session, avec une valeur en trois morceaux séparés par des points.

Pourquoi ? Un utilisateur modifie la valeur de ce cookie pour y mettre le login d'un autre salarié. Pourquoi le serveur refuse-t-il ce cookie ?

Votre réponse :

POINT DE CONTRÔLE C

  • Montrez au formateur votre grille de l'étape 15 et votre tableau de l'étape 18.
  • Le formateur a validé. Passez à la partie D.

PARTIE D — Lire une erreur serveur

Étapes 19 à 22, environ 20 min. À la fin, vous savez où lire une erreur, et ce que voit l'utilisateur en production.

Besoin d'un rappel ? Revoir le cours : « Quand le serveur plante ».

19

Lire l'erreur dans le navigateur

Un collègue a écrit une page /bilan qui additionne les heures. Elle plante.

Vous devez voir : une page d'erreur de Flask, avec des morceaux du code de app.py.

20

Lire la même erreur dans le journal

Vous devez voir : la même trace, et une ligne qui se termine par "GET /bilan HTTP/1.1" 500 -.

21

Passer en mode production

python -m flask --app app run

Vous devez voir : seulement « Internal Server Error », et une phrase en anglais. Aucune ligne de code. La trace complète est toujours dans le terminal.

Pourquoi ? Pourquoi l'utilisateur ne doit-il pas voir la trace de l'étape 19 ?

Votre réponse :

22

Trouver dans la doc et corriger

La ligne fautive lit une clé qui n'existe pas. Le nom juste est dans la description des données.

Vous devez voir : la phrase « Total des heures saisies : » suivie d'un nombre d'heures.

POINT DE CONTRÔLE D

  • Montrez au formateur la page /bilan corrigée, et vos réponses aux étapes 19 à 21.
  • Le formateur a validé. Le labo est fini.

POUR ALLER PLUS LOIN

10 à 15 min, si vous avez fini avant la fin de la séance.

Un collègue a demandé à une IA d'écrire une page de connexion. Voici ce qu'elle a produit. Le code marche, mais il contient trois erreurs dangereuses.

@app.route("/connexion")
def connexion():
    login = request.args.get("login")
    mot_de_passe = request.args.get("mot_de_passe")
    if login and donnees.verifier_compte(login, mot_de_passe):
        session["login"] = login
        session["mot_de_passe"] = mot_de_passe
        return redirect(url_for("liste_chantiers"))
    return render_template("connexion.html", erreur=None)

if __name__ == "__main__":
    app.run(host="0.0.0.0", debug=True)
Ligne L'erreur Le danger

CE QU'IL FAUT RETENIR

  1. La vue passe les données au gabarit sous le nom exact que le gabarit attend.
  2. Chaque champ reçu est du texte : on le vérifie, on le convertit, et on prévoit l'échec.
  3. Une page protégée vérifie la session en premier. En production, pas de --debug : la trace reste dans le journal.
TP

L'interface de consultation des chantiers

TP-U6-S2-03

Contexte — Karim Benali vous appelle : « Mes techniciens veulent voir leurs chantiers depuis un navigateur. Pas le tableur, une vraie page. »

« Et Yann, notre commercial, veut voir ce que chaque service a fait chez ses clients. »

La base de données n'est pas encore branchée. Les données sont dans un fichier. Seuls les salariés connectés doivent voir les chantiers.

Règle IA de la séance

Scénario 2 : l'IA peut vous expliquer une notion ou un message d'erreur. Elle n'écrit ni vos vues ni vos gabarits.

Notez dans votre mini-rapport chaque question posée à une IA.

CE QUE VOUS RENDEZ

Ce que vous rendez Format Nom du fichier
Le dossier consultation, complet ZIP NOM_Prenom_TP-U6-S2-03_appli.zip
Le mini-rapport PDF NOM_Prenom_TP-U6-S2-03_rapport.pdf

Le mini-rapport : deux pages au maximum. Il prouve que c'est votre travail : chaque rubrique s'appuie sur ce qui s'est passé sur votre poste. Un texte général, sans traces de votre poste, ne compte pas.

  1. Mes captures : les quatre captures des parties A à D. Prenez-les en plein écran : l'heure de la barre des tâches doit se voir. Une ligne de légende sous chacune.
  2. Mon journal : connecté, faites ce parcours dans l'ordre : /chantiers, filtre « atelier », /chantiers/2, /chantiers/99, « Se déconnecter », /chantiers. Recopiez les lignes de votre terminal, sauf style.css et favicon.ico. Pour chacune, notez le code de réponse et ce qu'il veut dire, en une phrase.
  3. Mes trois erreurs : trois erreurs que vous avez vraiment rencontrées pendant le TP, dans le tableau ci-dessous.
  4. Une ligne de mon code : recopiez une ligne de votre vue liste(). Expliquez-la avec vos mots, en trois lignes au maximum.
  5. Ma grille de lisibilité : la grille de la partie D, remplie.
  6. Ce qui manque : ce qui ne marche pas, ce que vous avez essayé, les coups de pouce utilisés, et les questions posées à une IA.

Voici une ligne d'exemple pour la rubrique 3 :

Le message d'erreur, recopié Ce qui le causait Ce que j'ai changé
UndefinedError: 'chantiers' is undefined Ma vue passait liste= au gabarit, qui attend chantiers. J'ai écrit chantiers=chantiers dans render_template.

Vos trois erreurs :

Le message d'erreur, recopié Ce qui le causait Ce que j'ai changé

Comment ce sera évalué

Critère Ce qui est attendu
Les vues et les routes Les trois écrans marchent, le filtre aussi, et les pages sont protégées par la session.
La présentation de l'interface Les pages respectent la charte de lisibilité de l'annexe .
Présentation Document daté, nommé selon la convention, titres clairs, tableaux lisibles

Sans traces de votre poste, c'est-à-dire sans vos captures, votre journal ni vos erreurs, les deux premiers critères ne dépassent pas « Fragile ».

Et à l'oral — le formateur pointera une ligne de votre code ou de votre mini-rapport. Il vous demandera de l'expliquer, ou de refaire devant lui une erreur que vous racontez.

Ce dont vous disposez

Le plan du TP

Partie Ce que vous obtenez Temps conseillé
A — La page de liste la liste des chantiers 35 min
B — Le filtre par service la liste filtrée par l'adresse 25 min
C — La page de détail un chantier et ses interventions 35 min
D — La connexion et la lisibilité des pages protégées et conformes à la charte 35 min
Mini-rapport 20 min

Gardez 20 minutes à la fin pour le mini-rapport.

Bloqué ? Demandez un coup de pouce. Ça ne fait pas perdre de points.


PARTIE A — La page de liste

Environ 35 min.

Vous construisez l'écran 1 de l'annexe , sans filtre pour l'instant. Cette fois, vous écrivez la vue et le gabarit.

Besoin d'un rappel ? Revoir le cours : « Route, vue, gabarit : le chemin d'une page ».

Nom passé au gabarit Ce qu'il contient Où le gabarit l'utilise

Ce qui doit marcher à la fin de la partie A

POINT DE CONTRÔLE A

  • Le formateur a vu la liste des six chantiers. Passez à la partie B.

PARTIE B — Le filtre par service

Environ 25 min.

Yann veut voir les chantiers où un service est intervenu. Le choix du service passe dans l'adresse : /chantiers?service=atelier.

Besoin d'un rappel ? Revoir le cours : « Recevoir une saisie et la vérifier ».

Adresse demandée Nombre de chantiers attendu Nombre obtenu
/chantiers 6
/chantiers?service=atelier 5
/chantiers?service=bureau etudes 2
/chantiers?service=commercial 3
/chantiers?service=nimporte 6

Ce qui doit marcher à la fin de la partie B

POINT DE CONTRÔLE B

  • Le formateur a vu la grille de test remplie. Passez à la partie C.

PARTIE C — La page de détail

Environ 35 min.

Un clic sur le nom d'un chantier ouvre sa page. Elle montre le chantier et toutes ses interventions.

Adresse demandée Résultat attendu Résultat obtenu
/chantiers/2 3 interventions, dont une avec le blocage « acces refuse »
/chantiers/6 1 intervention, blocage « aucun »
/chantiers/99 page « Not Found », code 404 dans le journal
/chantiers/abc page « Not Found », code 404 dans le journal

Ce qui doit marcher à la fin de la partie C

POINT DE CONTRÔLE C

  • Le formateur a vu la page du chantier 2 et la page 404. Passez à la partie D.

PARTIE D — La connexion et la lisibilité

Environ 35 min.

Les chantiers ne sont visibles que par les salariés connectés. Puis vous relisez vos pages avec la charte.

Besoin d'un rappel ? Revoir le cours : « Se souvenir de l'utilisateur : la session ».

Règle de la charte Liste : respectée ? Détail : respectée ? Ce que vous avez corrigé
1
2
3
4
5
6
7
8

Ce qui doit marcher à la fin de la partie D

POINT DE CONTRÔLE D

  • Le formateur a vu une redirection vers la connexion, puis la grille remplie. Rédigez le mini-rapport.
Annexes

Tous les documents du module

Cliquez sur une annexe pour l'ouvrir dans le panneau de droite. Elle reste ouverte pendant que vous lisez.