Lecture 9
- Bienvenue !
- Statique vers Dynamique
- Flask
- Mise en page
- POST
- Frosh IMs
- Flask et SQL
- Session
- Store
- API
- JSON
- Résumé
Bienvenue !
- Au cours des semaines précédentes, vous avez appris de nombreux langages de programmation, techniques et stratégies.
- En effet, ce cours a été bien moins un cours C ou Python qu'un cours de programmation, afin que vous puissiez suivre les futures tendances.
- Ces dernières semaines, vous avez appris comment apprendre à programmer.
- Aujourd’hui, nous allons passer du HTML et CSS au CSS, à SQL, à Python et à JavaScript pour que vous puissiez créer vos propres applications Web.
Statique vers Dynamique
- Jusqu’à présent, tout le HTML que vous avez vu était préécrit et statique.
- Dans le passé, lorsque vous visitiez une page, le navigateur téléchargeait une page HTML que vous pouviez consulter.
- Les pages dynamiques font référence à la capacité de Python et des langages similaires à créer des fichiers HTML à la volée. Par conséquent, vous pouvez avoir des pages Web générées par des options sélectionnées par votre utilisateur.
- Vous avez déjà utilisé
http-server
pour fournir vos pages Web. Aujourd’hui, nous allons utiliser un nouveau serveur qui peut analyser une adresse Web et exécuter des actions en fonction de l’URL fournie.
Flask
- Flask est une bibliothèque tierce qui vous permet d’héberger des applications Web à l’aide du cadre Flask dans Python.
- Vous pouvez exécuter flask en exécutant
flask run
. - Pour ce faire, vous aurez besoin d’un fichier appelé
app.py
et d’un dossier appelétemplates
. -
Pour commencer, créez un dossier appelé
templates
et créez un fichier appeléindex.html
avec le code suivant :<!DOCTYPE html> <html lang="en"> <head> <meta name="viewport" content="initial-scale=1, width=device-width"> <title>hello</title> </head> <body> hello, {{ name }} </body> </html>
Remarquez le double
{{ name }}
qui est un espace réservé pour quelque chose qui sera fourni plus tard par notre serveur Flask. -
Ensuite, dans le même dossier que le dossier
templates
, créez un fichier appeléapp.py
et ajoutez le code suivant :# Salue l'utilisateur from flask import Flask, render_template, request app = Flask(__name__) @app.route("/") def index(): return render_template("index.html", name=request.args.get("name", "world"))
Remarquez que ce code définit
app
comme l’application Flask. Ensuite, il définit la route/
deapp
comme renvoyant le contenu deindex.html
avec l’argumentname
. Par défaut, la fonctionrequest.args.get
recherchera lename
fourni par l’utilisateur. Si aucun nom n’est fourni, il choisira par défautworld
. -
Enfin, ajoutez un fichier final dans le même dossier que
app.py
appelérequirements.txt
qui ne comporte qu'une seule ligne de code :Flask
Notez que seul
Flask
apparaît dans ce fichier. -
Vous pouvez exécuter ce fichier en saisissant
flask run
dans la fenêtre du terminal. Si Flask ne s'exécute pas, assurez-vous que la syntaxe de chacun des fichiers ci-dessus est correcte. De plus, si Flask ne s'exécute pas, assurez-vous que vos fichiers sont organisés comme suit :/templates index.html app.py requirements.txt
Une fois que l'exécution est lancée, vous serez invité à cliquer sur un lien. Une fois que vous accédez à cette page Web, essayez d'ajouter
?name=[Votre nom]
à l'URL de base dans la barre d'URL de votre navigateur. -
En améliorant notre programme, nous savons que la plupart des utilisateurs ne saisissent pas d'arguments dans la barre d'adresse. Au lieu de cela, les programmeurs comptent sur les utilisateurs pour remplir des formulaires sur des pages Web. En conséquence, nous pouvons modifier index.html comme suit :
<!DOCTYPE html> <html lang="en"> <head> <meta name="viewport" content="initial-scale=1, width=device-width"> <title>hello</title> </head> <body> <form action="/greet" method="get"> <input autocomplete="off" autofocus name="name" placeholder="Name" type="text"> <button type="submit">Greet</button> </form> </body> </html>
Notez qu'un formulaire est maintenant créé qui prend le nom de l'utilisateur, puis le transmet à une route appelée
/greet
. -
De plus, nous pouvons modifier
app.py
comme suit :# Ajoute un formulaire, une deuxième route from flask import Flask, render_template, request app = Flask(__name__) @app.route("/") def index(): return render_template("index.html") @app.route("/greet") def greet(): return render_template("greet.html", name=request.args.get("name", "world"))
Notez que le chemin d'accès par défaut affiche un formulaire permettant à l'utilisateur de saisir son nom. La route
/greet
transmet lenom
à cette page Web. -
Pour finaliser cette implémentation, vous aurez besoin d'un autre modèle pour
greet.html
comme suit :<!DOCTYPE html> <html lang="en"> <head> <meta name="viewport" content="initial-scale=1, width=device-width"> <title>hello</title> </head> <body> hello, {{ name }} </body> </html>
Notez que cette route affichera désormais le message d'accueil à l'utilisateur, suivi de son nom.
Disposition
- Nos deux pages Web,
index.html
etgreet.html
, possèdent en grande partie les mêmes données. Ne serait-il pas appréciable de permettre au corps d'être unique, mais de copier la même disposition de page en page ? -
Tout d'abord, créez un nouveau modèle appelé
layout.html
et écrivez le code suivant :<!DOCTYPE html> <html lang="en"> <head> <meta name="viewport" content="initial-scale=1, width=device-width"> <title>hello</title> </head> <body> {% block body %}{% endblock %} </body> </html>
Notez que
{% block body %}{% endblock %}
permet l'insertion d'un autre code provenant d'autres fichiers HTML. -
Ensuite, modifiez votre
index.html
comme suit :{% extends "layout.html" %} {% block body %} <form action="/greet" method="post"> <input autocomplete="off" autofocus name="name" placeholder="Name" type="text"> <button type="submit">Greet</button> </form> {% endblock %}
Notez que la ligne
{% extends "layout.html" %}
indique au serveur où obtenir la disposition de cette page. Puis,{% block body %}{% endblock %}
indique quel code insérer danslayout.html
. -
Enfin, modifiez
greet.html
comme suit :{% extends "layout.html" %} {% block body %} hello, {{ name }} {% endblock %}
Remarquez comme ce code est plus court et concis.
POST
- Vous pouvez imaginer des scénarios où il n'est pas sûr d'utiliser
get
, car les noms d'utilisateur et les mots de passe apparaîtront dans l'URL. -
Nous pouvons utiliser la méthode
post
pour résoudre ce problème en modifiantapp.py
comme suit :# Bascule vers POST from flask import Flask, render_template, request app = Flask(__name__) @app.route("/") def index(): return render_template("index.html") @app.route("/greet", methods=["POST"]) def greet(): return render_template("greet.html", name=request.form.get("name", "world"))
Remarquez que
POST
est ajouté à la route/greet
et que nous utilisonsrequest.form.get
plutôt querequest.args.get
. -
Cela indique au serveur de rechercher plus en profondeur dans l'enveloppe virtuelle et de ne pas dévoiler les éléments dans
post
dans l'URL. -
Néanmoins, ce code peut être amélioré en utilisant une seule route pour
get
etpost
. Pour ce faire, modifiezapp.py
comme suit :# Utilise une seule route from flask import Flask, render_template, request app = Flask(__name__) @app.route("/", methods=["GET", "POST"]) def index(): if request.method == "POST": return render_template("greet.html", name=request.form.get("name", "world")) return render_template("index.html")
Remarquez que
get
etpost
sont tous deux effectués dans un seul routage. Cependant,request.method
est utilisé pour router correctement en fonction du type de routage demandé par l'utilisateur.
Frosh IM
- Frosh IM ou froshims est une application Web qui permet aux étudiants de s'inscrire à des sports intra-muros.
-
Créez un dossier en tapant
mkdir froshims
dans la fenêtre du terminal. Puis, tapezcd froshims
pour naviguer vers ce dossier. À l'intérieur, créez un répertoire appelé templates en tapantmkdir templates
. Enfin, tapezcode app.py
et écrivez le code suivant :# Implémente un formulaire d'inscription à l'aide d'un menu de sélection from flask import Flask, render_template, request app = Flask(__name__) SPORTS = [ "Basketball", "Soccer", "Ultimate Frisbee" ] @app.route("/") def index(): return render_template("index.html", sports=SPORTS) @app.route("/register", methods=["POST"]) def register(): # Valider la soumission if not request.form.get("name") or request.form.get("sport") not in SPORTS: return render_template("failure.html") # Confirmer l'inscription return render_template("success.html")
Notez qu'une option
echec
est fournie, de telle sorte qu'un message d'échec s'affiche pour l'utilisateur si le champnom
ousport
n'est pas correctement rempli. -
Ensuite, créez un fichier dans le dossier
templates
appeléindex.html
en tapantcode templates/index.html
et écrivez le code suivant :{% extends "layout.html" %} {% block body %} <h1>Register</h1> <form action="/register" method="post"> <input autocomplete="off" autofocus name="name" placeholder="Name" type="text"> <select name="sport"> <option disabled selected>Sport</option> {% for sport in sports %} <option value="{{ sport }}">{{ sport }}</option> {% endfor %} </select> <button type="submit">Register</button> </form> {% endblock %}
-
Ensuite, créez un fichier appelé
layout.html
en tapantcode templates/layout.html
et écrivez le code suivant :<!DOCTYPE html> <html lang="en"> <head> <meta name="viewport" content="initial-scale=1, width=device-width"> <title>froshims</title> </head> <body> {% block body %}{% endblock %} </body> </html>
-
Quatrièmement, créez un fichier dans les modèles appelés
success.html
comme suit :{% extends "layout.html" %} {% block body %} You are registered! {% endblock %}
-
Enfin, créez un fichier dans les modèles appelés
failure.html
comme suit :{% extends "layout.html" %} {% block body %} You are not registered! {% endblock %}
-
Vous pouvez imaginer que nous pourrions accepter l'inscription de différents inscrits. Nous pouvons améliorer
app.py
comme suit :# Implémente un formulaire d'inscription, stockant les inscrits dans un dictionnaire, avec des messages d'erreur de flask importe Flask, redirection, render_template, demande application = Flacon (__name__) INSCRITS = {} SPORTS = [ « Basketball », « Football », « Ultimate Frisbee » ] @application.route (« / ») def index() : renvoyer render_template (« index.html », sports=SPORTS) @application.route (« / inscrire ») def enregistrer() : # Valider le nom nom = demander.formule.obtenir (« nom ») si pas nom : retourne render_template (« error.html », message=« Nom manquant ») # Valider le sport sport = demander.formule.obtenir (« sport ») si pas sport : retourne render_template (« error.html », message=« Sport manquant ») si le sport n'est pas dans les SPORTS : retourne render_template (« error.html », message=« Sport non valide ») # Mémoriser l'inscrit INSCRITS [nom] = sport # Confirmer l'inscription retourner la redirection (« / inscrits ») @application.route (« / inscrits ») def inscrits() : renvoyer render_template (« registrants.html », registrants=REGISTRANTS)
Remarquez qu'un dictionnaire appelé
REGISTRANTS
est utilisé pour enregistrer le sport sélectionné parREGISTRANTS[name]
. Remarquez également queregistrants=REGISTRANTS
transmet le dictionnaire à ce modèle. -
En outre, créez un nouveau modèle appelé
registrants.html
comme suit :{% étend « layout.html » %} {% block body %} <h1>Inscrits</h1> <table> <thead> <tr> <th>Nom</th> <th>Sport</th> </tr> </thead> <tbody> {% pour nom dans les inscrits %} <tr> <td>{{ nom }}</td> <td>{{inscrits[nom]}}</td> </tr> {% fin pour %} </tbody> </table> {% fin du bloc %}
Remarquez que
{% pour nom dans les inscrits %}...{% fin pour %}
itérera sur chacun des inscrits. Très puissant de pouvoir itérer sur une page Web dynamique ! -
En exécutant
flask run
et en saisissant de nombreux noms et sports, vous pouvez accéder à/registrants
pour afficher les données qui ont été enregistrées. - Vous avez maintenant une application Web ! Cependant, il existe certaines failles de sécurité ! Étant donné que tout se trouve côté client, un adversaire pourrait modifier le HTML et pirater un site Web. De plus, ces données ne persisteront pas si le serveur est arrêté. Pourrait-il y avoir un moyen de faire en sorte que nos données persistent même lorsque le serveur redémarre ?
Flask et SQL
- Tout comme nous avons vu comment Python peut s'interfacer avec une base de données SQL, nous pouvons combiner la puissance de Flask, Python et SQL pour créer une application web dans laquelle les données persisteront !
- Pour mettre cela en œuvre, vous devrez prendre un certain nombre de mesures.
-
Tout d'abord, modifiez requirements.txt comme suit :
cs50 Flask
-
Modifiez index.html comme suit :
{% extends "layout.html" %} {% block body %} <h1>S'inscrire</h1> <form action="/register" method="post"> <input autocomplete="off" autofocus name="name" placeholder="Nom" type="text"> {% for sport in sports %} <input name="sport" type="radio" value="{{ sport }}"> {{ sport }} {% endfor %} <button type="submit">S'inscrire</button> </form> {% endblock %}
-
Modifiez layout.html comme suit :
<!DOCTYPE html> <html lang="en"> <head> <meta name="viewport" content="initial-scale=1, width=device-width"> <title>froshims</title> </head> <body> {% block body %}{% endblock %} </body> </html>
-
Assurez-vous que failure.html apparaît comme suit :
{% extends "layout.html" %} {% block body %} Vous n'êtes pas inscrit ! {% endblock %}
-
Modifiez registrants.html pour qu'il apparaisse comme suit :
{% extends "layout.html" %} {% block body %} <h1>Inscrits</h1> <table> <thead> <tr> <th>Nom</th> <th>Sport</th> <th></th> </tr> </thead> <tbody> {% for registrant in registrants %} <tr> <td>{{ registrant.name }}</td> <td>{{ registrant.sport }}</td> <td> <form action="/deregister" method="post"> <input name="id" type="hidden" value="{{ registrant.id }}"> <button type="submit">Se désinscrire</button> </form> </td> </tr> {% endfor %} </tbody> </table> {% endblock %}
Notez qu'une valeur cachée
registrant.id
est incluse pour qu'il soit possible d'utiliser cetid
plus tard dansapp.py
-
Enfin, modifiez
app.py
comme suit :# Implémente un formulaire d'inscriptions, stockant les inscrits dans une base de données SQLite, avec option de désinscription de cs50 import SQL de flask import Flask, redirect, render_template, request app = Flask(__name__) db = SQL("sqlite:///froshims.db") SPORTS = [ "Basketball", "Soccer", "Ultimate Frisbee" ] @app.route("/") def index(): return render_template("index.html", sports=SPORTS) @app.route("/deregister", méthodes=["POST"]) def deregister(): # Oublie l'inscrit id = request.form.get("id") si id : db.execute("DELETE FROM registrants WHERE id = ?", id) return redirect("/registrants") @app.route("/register", méthodes=["POST"]) def register(): # Valide la soumission nom = request.form.get("nom") sport = request.form.get("sport") si pas nom ou sport pas dans SPORTS : return render_template("failure.html") # Se souviens de l'inscrit db.execute("INSERT INTO registrants (nom, sport) VALUES(?, ?)", nom, sport) # Confirme l'inscription return redirect("/registrants") @app.route("/registrants") def registrants(): inscrits = db.execute("SELECT * FROM registrants") return render_template("registrants.html", inscrits=inscrits)
Notez que la bibliothèque
cs50
est utilisée. Une route est incluse pourregister
pour la méthodepost
. Cette route prendra le nom et le sport pris dans le formulaire d'inscription et exécutera une requête SQL pour ajouter lenom
et lesport
à la tableregistrants
. La routederegister
est reliée à une requête SQL qui récupérera l'identifiant de l'utilisateur et utilisera cette information pour le désinscrire. -
Vous pouvez en lire plus dans la documentation Flask.
Séance
- Bien que le code ci-dessus soit utile du point de vue administratif, où un administrateur du back-office pourrait ajouter et supprimer des personnes de la base de données, on peut imaginer que ce code n'est pas sûr à implémenter sur un serveur public.
- D'une part, de mauvais acteurs pourraient prendre des décisions au nom d'autres utilisateurs en appuyant sur le bouton de désinscription, supprimant ainsi efficacement leur réponse enregistrée du serveur.
- Les services Web comme Google utilisent les informations d'identification de connexion pour s'assurer que les utilisateurs n'ont accès qu'aux bonnes données.
- Nous pouvons en fait implémenter cela nous-mêmes à l'aide de cookies. Les cookies sont de petits fichiers qui sont stockés sur votre ordinateur, de sorte que votre ordinateur puisse communiquer avec le serveur et dire effectivement : « Je suis un utilisateur autorisé qui s'est déjà connecté ».
- Dans la forme la plus simple, nous pouvons implémenter cela en créant un dossier appelé
login
, puis en ajoutant les fichiers suivants. -
Tout d'abord, créez un fichier appelé
requirements.txt
qui se lit comme suit :Flask Flask-Session
Notez qu'en plus de
Flask
, nous incluons égalementFlask-Session
, qui est nécessaire pour prendre en charge les sessions de connexion. -
Deuxièmement, dans un dossier
templates
, créez un fichier appelélayout.html
qui apparaît comme suit :<!DOCTYPE html> <html lang="en"> <head> <meta name="viewport" content="initial-scale=1, width=device-width"> <title>store</title> </head> <body> {% block body %}{% endblock %} </body> </html>
Notez que cela fournit une mise en page très simple avec un titre et un corps.
-
Troisièmement, créez un fichier dans le dossier
templates
appeléindex.html
qui apparaît comme suit :{% extends "layout.html" %} {% block body %} {% if session["name"] %} Vous êtes connecté en tant que {{ session["name"] }}. <a href="/logout">Déconnexion</a>. {% else %} Vous n'êtes pas connecté. <a href="/login">Connectez-vous</a>. {% endif %} {% endblock %}
Notez que ce fichier regarde si
session["name"]
existe. Si c'est le cas, il affichera un message de bienvenue. Sinon, il vous recommandera de parcourir une page pour vous connecter. -
Quatrièmement, créez un fichier appelé
login.html
et ajoutez le code suivant :{% extends "layout.html" %} {% block body %} <form action="/login" method="post"> <input autocomplete="off" autofocus name="name" placeholder="Nom" type="text"> <button type="submit">Connexion</button> </form> {% endblock %}
Notez qu'il s'agit de la mise en page d'une page de connexion basique.
-
Finalement, créez un fichier dans le dossier
login
appeléapp.py
et écrivez le code comme suit :from flask import Flask, redirect, render_template, request, session from flask_session import Session # Configurer l'application app = Flask(__name__) # Configurer la session app.config["SESSION_PERMANENT"] = False app.config["SESSION_TYPE"] = "filesystem" Session(app) @app.route("/") def index(): if not session.get("name"): return redirect("/login") return render_template("index.html") @app.route("/login", methods=["GET", "POST"]) def login(): if request.method == "POST": session["name"] = request.form.get("name") return redirect("/") return render_template("login.html") @app.route("/logout") def logout(): session["name"] = None return redirect("/")
Notez les imports modifiés en haut du fichier, y compris
session
, qui vous permettra de prendre en charge les sessions. Le plus important, c'est commentsession["name"]
est utilisé dans les cheminslogin
etlogout
. Le cheminlogin
attribuera le nom de connexion fourni et l'attribuera àsession["name"]
. Cependant, dans le cheminlogout
, la déconnexion est implémentée en définissant simplementsession["name"]
àNone
. -
Vous pouvez en savoir plus sur les sessions dans la documentation Flask.
Magasin
- Passons à un exemple final de l'utilisation de la capacité de Flask à activer une session.
-
Nous avons examiné le code suivant pour
store
dansapp.py
. Le code suivant était affiché :from cs50 import SQL from flask import Flask, redirect, render_template, request, session from flask_session import Session # Configurer l'application app = Flask(__name__) # Se connecter à la base de données db = SQL("sqlite:///store.db") # Configurer la session app.config["SESSION_PERMANENT"] = False app.config["SESSION_TYPE"] = "filesystem" Session(app) @app.route("/") def index(): books = db.execute("SELECT * FROM books") return render_template("books.html", books=books) @app.route("/cart", methods=["GET", "POST"]) def cart(): # S'assurer que le panier existe if "cart" not in session: session["cart"] = [] # POST if request.method == "POST": id = request.form.get("id") if id: session["cart"].append(id) return redirect("/cart") # GET books = db.execute("SELECT * FROM books WHERE id IN (?)", session["cart"]) return render_template("cart.html", books=books)
Notez que
cart
est implémenté à l'aide d'une liste. Les éléments peuvent être ajoutés à cette liste à l'aide des boutonsAjouter au panier
dansbooks.html
. Lorsque vous cliquez sur un tel bouton, la méthodepost
est invoquée, où leid
de l'élément est ajouté aupanier
. Lors de la visualisation du panier, on invoque la méthodeget
, SQL est exécuté pour afficher une liste des livres dans le panier.
API
- Une interface de programmation d'applications ou API est une série de spécifications qui vous permettent d'interagir avec un autre service. Par exemple, nous pourrions utiliser l'API d'IMDB pour interagir avec leur base de données. Nous pourrions même intégrer des API pour gérer des types spécifiques de données téléchargeables depuis un serveur.
- Nous avons examiné un exemple appelé
shows
. -
En examinant
app.py
, nous avons vu ce qui suit :# Recherches des séries à l'aide d'Ajax from cs50 import SQL from flask import Flask, render_template, request app = Flask(__name__) db = SQL("sqlite:///shows.db") @app.route("/") def index(): return render_template("index.html") @app.route("/search") def search(): q = request.args.get("q") if q: shows = db.execute("SELECT * FROM shows WHERE title LIKE ? LIMIT 50", "%" + q + "%") else: shows = [] return render_template("search.html", shows=shows)
Notez que la route
search
exécute une requête SQL. -
En examinant
search.html
, vous remarquerez que c'est très simple :{% for show in shows %} <li>{{ show["title"] }}</li> {% endfor %}
Notez qu'il fournit une liste à puces.
-
Enfin, en examinant
index.html
, notez que le code AJAX est utilisé pour alimenter la recherche :<!DOCTYPE html> <html lang="en"> <head> <meta name="viewport" content="initial-scale=1, width=device-width"> <title>shows</title> </head> <body> <input autocomplete="off" autofocus placeholder="Requête" type="search"> <ul></ul> <script> let input = document.querySelector('input'); input.addEventListener('input', async function() { let response = await fetch('/search?q=' + input.value); let shows = await response.text(); document.querySelector('ul').innerHTML = shows; }); </script> </body> </html>
Notez qu'un écouteur d'événements est utilisé pour interroger dynamiquement le serveur afin de fournir une liste correspondant au titre fourni. Cela localisera la balise
ul
dans le code HTML et modifiera la page Web en conséquence pour inclure la liste des correspondances. -
Vous pouvez en savoir plus dans la documentation AJAX.
JSON
- JavaScript Object Notation ou JSON est un fichier texte de dictionnaires avec des clés et des valeurs. C'est une manière brute et conviviale pour obtenir de nombreuses données.
- JSON est un moyen très utile de récupérer des données depuis le serveur.
-
Vous pouvez le voir en action dans le fichier
index.html
que nous avons examiné ensemble :<!DOCTYPE html> <html lang="en"> <head> <meta name="viewport" content="initial-scale=1, width=device-width"> <title>shows</title> </head> <body> <input autocomplete="off" autofocus placeholder="Query" type="text"> <ul></ul> <script> let input = document.querySelector('input'); input.addEventListener('input', async function() { let response = await fetch('/search?q=' + input.value); let shows = await response.json(); let html = ''; for (let id in shows) { let title = shows[id].title.replace('<', '<').replace('&', '&'); html += '<li>' + title + '</li>'; } document.querySelector('ul').innerHTML = html; }); </script> </body> </html>
Bien que le code ci-dessus puisse sembler quelque peu cryptique, il vous fournit un point de départ pour que vous puissiez effectuer des recherches sur JSON par vous-même afin de voir comment celui-ci peut être implémenté dans vos propres applications Web.
-
Vous pouvez en lire plus dans la documentation JSON.
Résumé
Dans cette leçon, vous avez appris comment utiliser Python, SQL et Flask pour créer des applications Web. Plus précisément, nous avons abordé…
- GET
- POST
- Flask
- Session
- AJAX
- JSON
À bientôt pour notre conférence finale !