
Créer une application todo avec Flask
Dans ce guide, je te montre comment construire une application todolist en partant de zéro avec Flask
Flask est un micro-framework développé avec Python et maintenu par la team PalletsProjects.
C'est aussi eux qui sont derrière des librairies bien connues comme Click qui te permet de créer des interfaces en ligne de commandes.
Flask est intéressant car il va te permettre de construire les fondations de ton application web très rapidement sans t'imposer quoi que ce soit !
Personnellement, c'est une librairie que j'aime beaucoup. Elle est très accessible et facile à prendre en main. Cela permet de bien intégrer certains concepts de base avant de se plonger dans une librairie plus complexe comme Django.
👉 Je trouve que c'est juste parfait si tu débutes dans le développement web !
Aujourd'hui, on va voir les fondamentaux de Flask au travers d'une todo-app qu'on va développer en partant de zéro.
C'est un projet que j'adore réaliser à chaque fois que je découvre une nouvelle techno, tu verras que c'est très instructif.
On y va !
Aller, le premier truc qu'on va faire, c'est créer un nouveau répertoire pour notre application, créer un nouvel environnement virtuel et y installer flask.
mkdir todo_app && cd todo_app
python -m venv env
source env/bin/activate
pip install flask
Tu vas ensuite ouvrir ça dans ton éditeur de code préféré, pour ma part ce sera sur Visual Studio Code.
On va créer un fichier app.py
à l'intérieur de notre dossier todo_app et y écrire notre première méthode :
# todo_app/app.py
from flask import Flask
app = Flask(__name__) # Crée une instance de la classe Flask, c'est notre application
@app.route("/")
def index(): # Méthode appelée quand on se rend sur la route "/"
return "Hello World!"
Pour lancer l'application, il faut d'abord indiquer à Flask ce qu'il doit exécuter en exportant ces deux variables d'environnement :
export FLASK_APP=app.py
export FLASK_ENV=development
Et pour lancer le serveur, c'est très simple :
flask run
Tu n'as plus qu'à cliquer sur l'adresse de ton serveur local qui s'affiche dans ton terminal.
Si tout s'est bien passé de ton côté, tu devrais pouvoir lire 'Hello World' sur ton écran 👍

Notre application sera très simple.
Le but étant de te faire découvrir Flask, je ne vais pas t'embêter avec des notions trop avancées.
Cela pourra faire l'objet d'un autre article si celui-ci a du succès !
Du coup, on aura les quatre fonctionnalités de base de toute application :
- Ajouter un élément
- Afficher des éléments
- Mettre à jour un élément
- Supprimer un élément
C'est ce qu'on appelle un CRUD (Create, Read, Update, Delete) et c'est tout ce dont on va avoir besoin aujourd'hui.
Pour stocker nos données, on va utiliser SQLite3 qui est déjà disponible dans la librairie standard de Python et aussi une extension Flask qui s'appelle flask-sqlalchemy pour gérer nos interactions avec la base de données.
Du coup, on retourne rapidement dans le terminal pour installer ça (n'oublie pas d'activer ton environnement virtuel) :
pip install flask_sqlalchemy
Une fois que c'est fait, on va dans notre fichier app.py
pour définir le modèle qui représentera notre base de données :
# todo_app/app.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
app = Flask(__name__) # Crée un instance de la classe Flask, c'est notre app
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///todo.db' # Nom de la bdd
db = SQLAlchemy(app) # Lie notre app à SQLAlchemy
class Task(db.Model): # Modèle
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), nullable=False)
created_at = db.Column(db.DateTime, nullable=False, default=datetime.now)
@app.route('/')
def index(): # Méthode appelée lorsqu'on se rend sur la route '/'
return 'Hello World!'
J'ai importé SQLAlchemy depuis flask_sqlalchemy
et également le module datetime
.
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
Ensuite j'ai indiqué le nom de la base de données que je veux créer et je l'ai assigné à une variable de configuration SQLALCHEMY_DATABASE_URI
dans le dictionnaire app.config
.
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///todo.db' # Nom de la bdd
Derrière, j'ai lié mon application à SQLAlchemy (db = SQLAlchemy(app)
) et j'ai créé une classe Task
qui hérite de db.Model
dans laquelle j'ai défini tous les champs qui vont composer ma table.
- Un champ
id
pour stocker l'identifiant de la tâche. - Un champ
name
pour le nom. - Un champ
created_at
pour stocker la date de création de chaque tâche.
class Task(db.Model): # Modèle
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), nullable=False)
created_at = db.Column(db.DateTime, nullable=False, default=datetime.now)
Le modèle est écrit, il ne reste plus qu'à créer la base de données !
Pour ça, ré-ouvre ton terminal, lance python et exécute cette commande :
>>> from app import db, Task
>>> db.create_all()
La méthode create_all()
va créer la table en se basant sur la définition écrite dans notre modèle Task
, plutôt sympa !
Pour vérifier que tout a fonctionné correctement, on peut aller jeter un oeil dans sqlite3 :
sqlite3 todo.db
sqlite> .table
task
sqlite> .schema task
CREATE TABLE task (
id INTEGER NOT NULL,
name VARCHAR(50) NOT NULL,
created_at DATETIME NOT NULL,
PRIMARY KEY (id)
);
Tout semble ok, on va donc ajouter quelques tâches d'exemple pour peupler notre table !
On retourne dans l'interpréteur python :
>>> from app import db, Task
>>> task1 = Task(name='Apprendre Python')
>>> task2 = Task(name='Faire les courses')
>>> task3 = Task(name='Sortir le chien')
>>> db.session.add(task1)
>>> db.session.add(task2)
>>> db.session.add(task3)
>>> db.session.commit()
On continue !
Maintenant qu'on a fait ça, on aimerait bien pouvoir lire ce qu'il y a dans notre base de données et les afficher sur l'écran de l'utilisateur.
Pour ça, on va interroger notre base via notre modèle Task
pour récupérer toutes les tâches puis envoyer tout ça vers un template HTML.
Je t'explique juste après !
# todo_app/app.py
from flask import render_template
@app.route('/')
def index():
tasks = Task.query.order_by(Task.created_at).all()
return render_template('index.html', tasks=tasks)
Je fais une requête vers ma base de donnée avec la méthode query
, je précise que je veux les récupérer par ordre de création avec order_by()
puis que je veux tout récupérer avec la méthode all()
:
tasks = Task.query.order_by(Task.created_at).all()
Ensuite j'utilise la fonction render_template
de Flask pour envoyer les données vers un template (n'oublie pas de l'importer).
Le premier paramètre doit correspondre au nom du template et ce qui vient derrière, c'est toutes les données que tu veux transmettre :
return render_template('index.html', tasks=tasks)
Pour le coup, on veut juste envoyer notre liste de tâches !
Maintenant, on va créer ce template en HTML ! Dans ton répertoire todo_app, crée un nouveau dossier templates puis un fichier index.html à l'intérieur de ce dossier.
Je te laisse créer ce template rapidement avec le style qui te convient ! Ou tu peux utiliser le mien si tu préfères :
<!-- todo_app/templates/index.html -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Todo App</title>
</head>
<body>
<main>
<div id="tasks">
{% if tasks %}
{% for task in tasks %}
<div id="task">
<p>{{ task.name }}</p>
</div>
{% endfor %}
{% else %}
<p style="font-size: 16px; text-align: center;">Super, vous n'avez plus rien à faire ✌️</p>
{% endif %}
</div>
</main>
</body>
</html>
J'utilise le moteur de templating Jinja qui est installé avec Flask pour pouvoir implémenter un peu de logique en Python dans mon template.
Dans mon cas, je fais une boucle for
pour itérer sur ma liste de tâches et j'affiche simplement le nom de chaque tâche. Si ma liste est vide j'affiche un message d'information.
Ce que tu dois retenir quant à l'utilisation de Jinja, c'est :
{{ ... }}
→ Pour afficher des données que tu as envoyé dans la vue avec la fonctionrender_template
.{% ... %}
→ Pour créer des boucles, structures conditionnelles, bref la logique de ton code.
À ce stade, tu devrais pouvoir afficher la liste de toutes les tâches dans ton navigateur, essaie un peu pour voir si tout fonctionne correctement !
On rentre dans le vif du sujet !
Pour ajouter un élément dans notre todo_app, on va avoir besoin d'un formulaire dans lequel l'utilisateur pourra rentrer sa tâche.
<!-- todo_app/templates/index.html -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Todo App</title>
</head>
<body>
<main>
<form action="{{ url_for('index') }}" , method="POST">
<input type="text" name="name" id="task" placeholder="Faire les courses...">
<input type="submit" value="Add">
</form>
<div id="tasks">
{% if tasks %}
{% for task in tasks %}
<div id="task">
<p>{{ task.name }}</p>
</div>
{%endfor %}
{% else %}
<p style="font-size: 16px; text-align: center;">Super, vous n'avez plus rien à faire ✌️</p>
{% endif %}
</div>
</main>
</body>
</html>
Le truc important à noter ici, c'est ce qu'il y a dans l'attribut action de mon formulaire :
{{ url_for('index') }}
Quand j'écris ça, j'indique à Flask quelle route utiliser pour traiter les données du formulaire. Pour le reste, c'est un formulaire tout ce qu'il y a de plus classique !
url_for
va appelé notre vue index (vu qu'on lui a passé la chaîne de caractères 'index'
). C'est dans cette vue que l'on va traiter les données du formulaire.
Je te conseille d'ailleurs de toujours utiliser url_for
pour appeler tes vues ! N'écris jamais les routes en dur.
Et du coup, du côté de notre fichier app.py
:
from flask import Flask, request, redirect, url_for, render_template
@app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
name = request.form.get('name')
task = Task(name=name)
db.session.add(task)
db.session.commit()
return redirect(url_for('index'))
else:
tasks = Task.query.order_by(Task.created_at).all()
return render_template('index.html', tasks=tasks)
Il se passe pas mal de choses ici !
Je dois d'abord préciser les méthodes HTTP autorisées sur ma route '/'
. Comme on reçoit les données d'un formulaire, je dois pouvoir récupérer du GET mais aussi du POST. Je l'indique donc dans le décorateur au paramètre methods
:
@app.route('/', methods=['GET', 'POST'])
Ensuite, je vais récupérer les informations du formulaire grâce à request
:
name = request.form.get('name')
C'est une instance de la classe Request
contenue dans Flask et qui nous permet de récupérer des données entrantes comme les arguments, la route utilisée ou dans notre cas les données envoyées par un formulaire.
Après ça, je vais créer cette tâche dans ma base de données en passant par mon modèle Task
:
task = Task(name=name)
Il ne faut pas oublier d'ajouter notre objet à la session puis de faire un db.session.commit()
pour enregistrer la tâche dans la base de données :
db.session.add(task)
db.session.commit()
Enfin, il ne me reste plus qu'à rediriger l'utilisateur vers la page d'accueil en utilisant la fonction redirect
et url_for
que tu connais déjà.
Je te laisse ajouter quelques tâches !
Avant de passer à la suite, je vais te donner accès à ma feuille de style pour que notre application ressemble à quelque chose !
Pour cela, tu dois créer un dossier static
à la racine de ton projet (comme on l'avait fait pour le dossier templates
).
Ensuite, tu peux créer un nouveau fichier style.css
dans ce dossier static
.
On y stocke aussi les fichiers Javacript Tu dois obligatoirement appeler ce fichier static, c'est un autre convention de Flask.
Tu es libre de faire ce que tu veux, c'est juste au cas où 🙂
body {
background-color: #F7FAFC;
display: flex;
justify-content: center;
margin-top: 10%;
font-family: Arial, Helvetica, sans-serif;
}
main {
display: flex;
flex-direction: column;
background-color: white;
height: 100%;
}
form {
display: flex;
}
input[type=text] {
padding: 16px 32px;
box-sizing: border-box;
width: 100%;
font-size: 16px;
}
input::placeholder {
font-size: 16px;
}
input:focus {
outline: none;
}
input[type="submit"] {
background-color: #1E1E2D;
border: none;
color: white;
padding: 16px 32px;
text-decoration: none;
cursor: pointer;
font-size: 16px;
}
#tasks {
display: flex;
flex-direction: column;
width: 100%;
}
#task {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: baseline;
padding-left: 20px;
padding-right: 20px;
}
a {
color: #1E1E2D;
font-size: 16px;
font-weight: 600;
text-decoration: none;
}
p {
font-size: 16px;
}
Tu vois, j'ai personnalisé un peu les input de mon formulaire et j'ai joué avec flexbox pour centrer mes différents éléments !
Une fois que tu as écrit le code CSS, il faut lier tes templates à ce fichier CSS pour que le style s'applique.
Pour ça, ajoute la ligne suivante dans le fichier index.html
, au niveau de la balise <head>
:
<link rel="stylesheet" href="{{ url_for('static', filename='style.css')}}">
On utilise la fonction url_for()
pour dire à Flask : "Hé, voilà le dossier static qui contient mes feuilles de style. Je veux que tu appliques les styles du fichier style.css
."
Avec ça, ta todo app devrait ressembler à ça :

Pour mettre un jour notre liste de tâches, nous allons créer une nouvelle route dans le fichier app.py
Réservé aux membres abonnés

Pour pouvoir prendre des notes et ajouter des favoris tu dois être abonné à Docstring.
Voir les formulesBravo, tu es prêt à passer à la suite 👏

Tu as complété % du parcours.
Mes notes
Sauvegardé