Notre page d'accueil est assez vide, n'est-ce pas ? C'est le moment d'afficher le contenu d'index.html
!
Comprenez l'architecture modèle / vue / template
La structure d'un projet est une des clés qui garantit sa bonne évolution, exactement comme dans votre ordinateur ! Imaginez que vous repreniez le projet d'une autre personne, ou que vous ouvriez le vôtre dans six mois. Vous ne voulez pas perdre de temps à chercher comment il est organisé ! Vous voulez aller droit au but et apporter votre modification sans pour autant tout casser.
C'est pourquoi la plupart des frameworks pensés pour le web choisissent de séparer les fichiers dans plusieurs dossiers distincts. Cela peut paraître un peu contraignant au début mais ce sont de bonnes pratiques qui garantissent la bonne évolution de votre projet.
Flask, comme vous le savez désormais, n'impose pas vraiment de structure, il la propose. S'inspirant du modèle MVC (Modèle / Vue / Contrôleur) de bien des frameworks, il se base sur trois piliers fortement imbriqués : le modèle, la vue et le template. Voyons-les en détail.
Le modèle
Définition du modèle
Un modèle, comme nous l'avons vu dans le chapitre précédent, représente la structure de l'objet qui sera stocké dans la base de données.
Dans Flask, le modèle de chaque objet est représenté par une classe dans le fichier models.py
. La classe hérite de db.Model
et peut donc utiliser les méthodes de la classe parent.
Deux parties sont à distinguer dans un modèle :
la structure de l'item dans la base de données,
les attributs d'instance exposés.
L'appel à db.Column
crée une nouvelle colonne dans la table. Cela permet de filtrer les résultats d'une recherche par le contenu de cette colonne, par exemple, en utilisant la méthode TableName.query.filter_by(column_name=filter).first()
.
La méthode __init__
, elle, construit l'instance. Chaque attribut exposé à cet endroit pourra être modifié par la suite de la même manière que n'importe quelle instance de classe Python. Dans notre exemple, vous pouvez ainsi créer un nouvel élément content
dans la base en spécifiant sa description et son genre mais pas son id.
Exemple d'un modèle
Dans notre projet, models.py
contient un modèle Content
. Pour comprendre la différence entre ce qui est stocké en base et les valeurs retournées, je vous propose d'améliorer la gestion de l'attribut gender
.
Pour l'instant, nous enregistrons le genre en tant qu'entier mais nous ne connaissons pas (encore) le chiffre associé à chacun.
Nous verrons plus tard que Facebook renvoie les valeurs male
ou female
de l'utilisateur connecté. Il serait donc intéressant de pouvoir associer chaque chaîne de caractères à un nombre.
Créons une nouvelle classe, Gender
, qui héritera de enum
:
models.py
import enum
class Gender(enum.Enum):
female = 0
male = 1
other = 2
...
À présent, modifiez la valeur de la colonne gender
et l'initialisation de la base :
models.py
class Content(db.Model):
gender = db.Column(db.Enum(Gender), nullable=False)
def init_db():
db.session.add(Content("THIS IS SPARTAAAAAAA!!!", Gender['male']))
db.session.add(Content("What's your favorite scary movie?", Gender['female']))
Cela vous permet de créer des instances de cette manière : Content('Personne ne te faisait pitié, ni sur le moment, ni après, on était absolument sans défense devant toi.', Gender['male'])
.
Vous pourrez ensuite les retrouver de la manière suivante : Content.query.filter_by(gender=Gender['male'])
Le template
Un template est un fichier HTML qui peut recevoir des objets Python et qui est lié à une vue (nous y reviendrons). Il est placé dans le dossier templates
.
Concrètement, un template peut interpréter des variables et les afficher. Par exemple, nous pouvons "donner" la variable tom="Tom"
au template index.html
et ce dernier l'affichera à la place du prénom.
La vue
Les vues contenues dans le fichier views.py
jouent un rôle primordial : elles décident du contenu à afficher sur une page. Plus spécifiquement, ce sont elles qui génèrent le contenu à renvoyer aux requêtes qui leur sont adressées.
Une vue est une fonction qui renvoie une réponse à une requête HTTP. Toute fonction décorée par @app.route
est une vue.
On appelle route l'URL à laquelle va répondre la vue. Par exemple : /index
.
Afin de bien comprendre le rôle majeur des vues, intéressons-nous au cheminement d'une requête.
L'utilisateur tape
http://le-test-ultime.hello-birds.com
dans son navigateur puis appuie sur entrée. Cela génère une requête HTTP de cette forme :
GET/HTTP/1.1 Host:le-test-ultime.hello-birds.com
Le serveur à l'adresse
le-test-ultime.hello-birds.com
reçoit la requête HTTP. Il cherche dans les vues la route correspondant à la requête en fonction de la méthode HTTP utilisée et de l'URL.Le serveur exécute la vue : éventuellement, si la route contient des paramètres, ils sont passés comme arguments à la vue. Si une modification d'un objet en base de données est demandée, la vue fait appel au modèle. Si un template est demandé, la vue l'appelle.
Le serveur transmet la réponse HTTP de la vue au navigateur de l'utilisateur (appelé communément client).
Exemple de vue
Afin de mettre en pratique ce que nous venons d'explorer, affichons le contenu du template index.html
lorsque l'utilisateur arrive sur la page d'accueil.
views.py
from flask import Flask, render_template
...
@app.route('/')
def index():
return render_template('index.html')
Pourquoi ajouter un slash à la fin de l'URL ?
Les utilisateurs peuvent taper deux adresses différentes /index
ou /index/
. Hé oui, ce ne sont pas les mêmes ! Si vous déclarez une route sans le slash final (/index
), le serveur renverra une erreur 404 (page non trouvée) si l'utilisateur tape /index/
. Mais si vous déclarez une route avec le slash final (/index/)
et que l'utilisateur tape /index
, le serveur ne renverra pas d'erreur. Pensez-y ! :)
Le nom de vos routes doit être parlant ! Vous et l'utilisateur devez être en mesure de comprendre, en lisant l'URL, l'action qui sera effectuée. Par exemple, évitez d'utiliser une route qui aurait cette structure : /mise_a_jour_description_1
. Préférez l'url suivante : /contents/1/update
.
Il s'agit d'ailleurs d'une des bonnes pratiques les plus importantes à respecter lorsque l'on crée une application web : l'interface uniforme de l'architecture REST.
Appliquez REST : les dénominations conseillées des routes
REST est un acronyme pour Representational State Transfer. Cette architecture impose six contraintes que vous pouvez choisir de respecter :
Séparation client / serveur
Stateless (sans état)
Cacheable (cachable)
Layered system (système à plusieurs couches)
Uniform interface (interface uniforme)
Code on demand
Comment appliquer REST dans un projet Flask ? La question demanderait un cours entier ! Intéressez-vous en premier à l'interface uniforme et prenez le temps de bien comprendre comment créer de bonnes URL.
Récupérez le code du chapitre
Retrouvez le code de ce chapitre à cette adresse.
En résumé
Un projet Flask se structure à partir de trois piliers : le modèle, la vue et le template
Un modèle représente la structure d’un objet de la base de données
Un template est un fichier HTML dans lequel on peut récupérer des objets Python
Une vue a la responsabilité de traiter le contenu à envoyer à l’URL indiquée par la route
Une route dans Flask se crée en utilisant
@app.route
Maintenant que votre application web est accessible depuis la route créée et affiche le template et le modèle, vérifions que vous avez compris les notions de Flask avec un quiz. Vous verrez ensuite comment organiser votre projet en templates.