Absolument ! Voici une version complète de l’article tutoriel sur la création d’une To-Do List avec Flask et SQLAlchemy, en reprenant la structure amorcée et en la développant.

Tutoriel Complet : Créez Votre To-Do List avec Flask et SQLAlchemy
Salut les codeurs et codeuses ! Bienvenue sur Aircoding.fr, votre QG pour tout ce qui touche au développement web, qu’il soit assisté par l’IA ou construit pas à pas comme aujourd’hui. On va mettre les mains dans le code pour bâtir une application web classique mais essentielle : une To-Do List interactive en utilisant Flask, un micro-framework Python populaire, et SQLAlchemy, un ORM puissant pour gérer notre base de données.
Vous découvrez Flask, SQLAlchemy, ou même le développement web ? Pas de panique ! Ce tutoriel est conçu pour vous guider étape par étape, de la configuration initiale au déploiement d’une application fonctionnelle. L’objectif est simple : démystifier la création d’applications web et vous montrer que vous pouvez construire des outils utiles rapidement.
Prérequis
Avant de plonger dans le vif du sujet, assurez-vous d’avoir :
- Python installé sur votre machine (version 3.7 ou supérieure recommandée). Vous pouvez le télécharger sur python.org.
- pip, le gestionnaire de paquets Python (généralement inclus avec Python).
- Un terminal ou une invite de commandes.
- Des connaissances de base en Python et une compréhension élémentaire du HTML.
Ouvrez votre terminal et installons les bibliothèques nécessaires :
Bash
pip install Flask Flask-SQLAlchemy
Étape 1 : Initialisation du Projet et Modèle de Données
Commençons par organiser notre projet et définir comment nos tâches seront stockées.
- Créez un dossier pour votre projet, par exemple
flask_todo_app
. - À l’intérieur de ce dossier, créez un fichier nommé
app.py
. C’est ici que résidera notre code Flask principal. - Créez également un sous-dossier nommé
templates
. Flask cherchera nos fichiers HTML ici.
Ouvrez app.py
et insérons le code de base pour initialiser Flask et SQLAlchemy, ainsi que notre modèle de données pour les tâches :
Extrait de code
# app.py
from flask import Flask, render_template, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
import os
# Configuration de l'application Flask
app = Flask(__name__)
# Configuration de la base de données SQLAlchemy
# Utilisation de SQLite pour la simplicité, stocké dans le dossier 'instance'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///todo.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False # Désactive les notifications inutiles
# Initialisation de l'extension SQLAlchemy avec notre application Flask
db = SQLAlchemy(app)
# Définition du modèle de données pour une tâche (Todo)
class Todo(db.Model):
id = db.Column(db.Integer, primary_key=True) # Identifiant unique pour chaque tâche
content = db.Column(db.String(200), nullable=False) # Le texte de la tâche, ne peut pas être vide
completed = db.Column(db.Boolean, default=False) # Statut de la tâche (complétée ou non)
date_created = db.Column(db.DateTime, default=datetime.utcnow) # Date de création de la tâche
# Fonction pour représenter l'objet Todo quand on l'affiche (utile pour le debug)
def __repr__(self):
return f'<Task {self.id}>'
# Cette fonction s'assure que les tables de la base de données sont créées
# avant la première requête si elles n'existent pas déjà.
@app.before_request
def create_tables():
# Crée les tables UNIQUEMENT si elles n'existent pas déjà
# Pour éviter de le faire à chaque requête en production,
# on peut utiliser `flask db init`, `flask db migrate`, `flask db upgrade` avec Flask-Migrate
# Mais pour ce tutoriel simple, ceci est suffisant.
db.create_all()
# --- Les routes seront définies ici plus tard ---
# Point d'entrée pour exécuter l'application
if __name__ == '__main__':
app.run(debug=True) # debug=True active le mode débuggage (rechargement auto, infos d'erreur)
Explication rapide :
- On importe les modules nécessaires.
- On crée une instance de l’application
Flask
. - On configure l’URI de notre base de données (
sqlite:///todo.db
créera un fichiertodo.db
dans le dossierinstance
de votre projet) et on initialiseSQLAlchemy
. - On définit la classe
Todo
qui hérite dedb.Model
. Chaque attribut de classe défini avecdb.Column
correspondra à une colonne dans notre table de base de données. @app.before_request
avecdb.create_all()
assure que la tabletodo
est créée avant que l’application ne commence à traiter les requêtes.if __name__ == '__main__':
lance le serveur de développement Flask si le script est exécuté directement.debug=True
est très utile pendant le développement.
Étape 2 : Création des Templates HTML
Une application web a besoin d’une interface ! Utilisons Jinja2 (le moteur de template de Flask) pour créer nos pages HTML.
Créez un fichier base.html
dans le dossier templates
. Il servira de squelette commun à nos pages :
HTML
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}"> {# Lien vers un futur CSS #}
{% block head %}{% endblock %} {# Bloc pour ajouter des éléments spécifiques dans <head> #}
<title>{% block title %}To-Do App{% endblock %}</title> {# Bloc pour le titre de la page #}
</head>
<body>
<div class="container">
{% block body %}{% endblock %} {# Bloc principal où le contenu de la page sera inséré #}
</div>
</body>
</html>
Maintenant, créez index.html
dans le dossier templates
. C’est la page principale qui affichera notre liste et le formulaire d’ajout :
HTML
{% extends 'base.html' %} {# Hérite de notre template de base #}
{% block head %}
{# On pourrait ajouter des styles spécifiques ici si besoin #}
{% endblock %}
{% block title %}Ma To-Do List{% endblock %}
{% block body %}
<h1 style="text-align: center;">Ma Super To-Do List</h1>
<div class="form-container">
{# Formulaire pour ajouter une nouvelle tâche #}
<form method="POST" action="{{ url_for('index') }}"> {# L'action pointe vers notre route principale #}
<input type="text" name="content" id="content" placeholder="Entrez une nouvelle tâche..." required>
<button type="submit">Ajouter</button>
</form>
</div>
<div class="task-list">
{% if tasks|length < 1 %} {# Vérifie s'il y a des tâches #}
<p style="text-align: center;">Aucune tâche pour le moment. Ajoutez-en une !</p>
{% else %}
<table>
<thead>
<tr>
<th>Tâche</th>
<th>Ajoutée le</th>
<th>Statut</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{# Boucle pour afficher chaque tâche #}
{% for task in tasks %}
<tr class {% if task.completed %} "completed" {% endif %}>
<td>{{ task.content }}</td>
<td>{{ task.date_created.strftime('%d/%m/%Y') }}</td> {# Formate la date #}
<td>{% if task.completed %}Complétée{% else %}En cours{% endif %}</td>
<td>
{# Liens pour les actions (Update/Delete) #}
<a href="{{ url_for('update', id=task.id) }}">
{% if task.completed %}Annuler{% else %}Compléter{% endif %}
</a>
<a href="{{ url_for('delete', id=task.id) }}" class="delete-btn">Supprimer</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div>
{% endblock %}
Explication rapide :
{% extends 'base.html' %}
: Indique que ce template hérite debase.html
.{% block ... %}
et{% endblock %}
: Définissent des blocs qui peuvent être surchargés par les templates enfants ou qui insèrent du contenu.{{ ... }}
: Affiche la valeur d’une variable passée par Flask (ex:{{ task.content }}
).{% ... %}
: Utilisé pour les logiques de template (bouclesfor
, conditionsif
).url_for('...')
: Génère l’URL pour une fonction de route donnée (plus sûr que d’écrire les URLs en dur).- Le formulaire envoie les données via
POST
à la routeindex
. - La boucle
for
itère sur la listetasks
(qu’on passera depuis Flask) pour afficher chaque tâche. - Des liens pointent vers des futures routes
update
etdelete
, en passant l’ID de la tâche.
Étape 3 : Routes pour Afficher et Ajouter des Tâches
Retournons dans app.py
pour créer la logique qui gère l’affichage de la page d’accueil et l’ajout de nouvelles tâches. Modifiez/Ajoutez la route index
comme suit :
Python
# app.py (suite)
# Route principale : affiche les tâches et gère l'ajout de nouvelles tâches
@app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
# Si le formulaire est soumis (méthode POST)
task_content = request.form['content'] # Récupère le contenu du champ 'content' du formulaire
if task_content: # Vérifie si le contenu n'est pas vide
new_task = Todo(content=task_content) # Crée une nouvelle instance de tâche
try:
db.session.add(new_task) # Ajoute la nouvelle tâche à la session de la base de données
db.session.commit() # Sauvegarde les changements dans la base de données
return redirect(url_for('index')) # Redirige vers la page d'accueil (pour éviter de resoumettre le formulaire avec F5)
except Exception as e:
print(f"Erreur lors de l'ajout de la tâche: {e}")
return "Une erreur est survenue lors de l'ajout de votre tâche."
else:
# Gérer le cas où le contenu est vide si 'required' n'était pas dans le HTML
pass # Ou afficher un message d'erreur
else:
# Si on accède à la page normalement (méthode GET)
tasks = Todo.query.order_by(Todo.date_created).all() # Récupère toutes les tâches, triées par date
return render_template('index.html', tasks=tasks) # Rend le template HTML en passant la liste des tâches
# --- Le reste du code (model, if __name__...) reste identique ---
Explication :
@app.route('/', methods=['GET', 'POST'])
: Définit que cette fonction gère les requêtes GET et POST pour l’URL racine (/
).if request.method == 'POST':
: Vérifie si la requête est une soumission de formulaire.request.form['content']
: Accède aux données envoyées par le formulaire.new_task = Todo(...)
: Crée un nouvel objet tâche.db.session.add(new_task)
: Ajoute l’objet à la session SQLAlchemy (prêt à être sauvegardé).db.session.commit()
: Écrit les changements (la nouvelle tâche) dans la base de données.redirect(url_for('index'))
: Redirige l’utilisateur vers la page d’accueil après l’ajout (pattern Post/Redirect/Get).
else:
(pour les requêtes GET) :tasks = Todo.query.order_by(Todo.date_created).all()
: Interroge la base de données pour récupérer toutes les tâches (.all()
) et les trie (.order_by()
).render_template('index.html', tasks=tasks)
: Charge le fichierindex.html
et lui passe la variabletasks
contenant notre liste de tâches. Le template pourra alors l’utiliser ({% for task in tasks %}
).
À ce stade, vous devriez pouvoir lancer python app.py
dans votre terminal, ouvrir votre navigateur à l’adresse http://127.0.0.1:5000
(ou similaire), voir le titre, le formulaire, et ajouter des tâches qui s’afficheront dans la liste !
Étape 4 : Suppression des Tâches
Ajoutons la possibilité de supprimer une tâche. Nous avons déjà le lien dans index.html
. Créons la route correspondante dans app.py
:
Python
# app.py (suite)
# Route pour supprimer une tâche
@app.route('/delete/<int:id>')
def delete(id):
task_to_delete = Todo.query.get_or_404(id) # Récupère la tâche par son ID ou renvoie une erreur 404 si non trouvée
try:
db.session.delete(task_to_delete) # Supprime la tâche de la session
db.session.commit() # Sauvegarde le changement (suppression)
return redirect(url_for('index')) # Redirige vers l'accueil
except Exception as e:
print(f"Erreur lors de la suppression de la tâche: {e}")
return "Une erreur est survenue lors de la suppression de votre tâche."
# --- Le reste du code reste identique ---
Explication :
@app.route('/delete/<int:id>')
: Définit une route qui accepte un entier (id
) dans l’URL.Todo.query.get_or_404(id)
: Méthode très pratique pour récupérer un objet par sa clé primaire. Si l’ID n’existe pas, elle renvoie automatiquement une page 404 Not Found.db.session.delete(...)
etdb.session.commit()
: Suppriment l’objet de la base de données.
Étape 5 : Mise à Jour des Tâches (Compléter / Annuler)
Implémentons la fonctionnalité pour marquer une tâche comme complétée ou la remettre en cours. Le lien est déjà dans index.html
. Ajoutons la route dans app.py
:
Python
# app.py (suite)
# Route pour mettre à jour le statut d'une tâche (complétée/non complétée)
@app.route('/update/<int:id>')
def update(id):
task_to_update = Todo.query.get_or_404(id) # Récupère la tâche par ID ou 404
# Inverse le statut 'completed'
task_to_update.completed = not task_to_update.completed
try:
db.session.commit() # Sauvegarde le changement de statut
return redirect(url_for('index')) # Redirige vers l'accueil
except Exception as e:
print(f"Erreur lors de la mise à jour de la tâche: {e}")
return "Une erreur est survenue lors de la mise à jour de votre tâche."
# --- Le reste du code reste identique ---
Explication :
- La logique est similaire à la suppression, mais au lieu de supprimer, on modifie l’attribut
completed
de l’objettask_to_update
. task_to_update.completed = not task_to_update.completed
: C’est une façon simple d’inverser une valeur booléenne (True devient False, False devient True).db.session.commit()
sauvegarde ce changement dans la base de données.
Étape 6 (Optionnelle) : Un Peu de Style CSS
Notre application est fonctionnelle, mais un peu austère. Ajoutons quelques styles simples.
- Créez un dossier
static
à la racine de votre projet. - À l’intérieur de
static
, créez un sous-dossiercss
. - Dans
static/css
, créez un fichierstyle.css
. - Assurez-vous que
base.html
contient bien la ligne<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
dans le<head>
.
Ajoutez quelques règles CSS dans static/css/style.css
:
CSS
/* static/css/style.css */
body {
font-family: sans-serif;
margin: 0;
background-color: #f4f4f4;
}
.container {
max-width: 800px;
margin: 30px auto;
background: #fff;
padding: 20px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
border-radius: 8px;
}
h1 {
color: #333;
}
.form-container {
margin-bottom: 20px;
display: flex;
}
.form-container input[type="text"] {
flex-grow: 1;
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px 0 0 4px;
}
.form-container button {
padding: 10px 15px;
background-color: #5cb85c;
color: white;
border: none;
border-radius: 0 4px 4px 0;
cursor: pointer;
}
.form-container button:hover {
background-color: #4cae4c;
}
.task-list table {
width: 100%;
border-collapse: collapse;
}
.task-list th, .task-list td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
.task-list th {
background-color: #f2f2f2;
}
.task-list tr.completed td:first-child {
text-decoration: line-through;
color: grey;
}
.task-list td a {
margin-right: 5px;
text-decoration: none;
color: #0275d8;
}
.task-list td a:hover {
text-decoration: underline;
}
.task-list td a.delete-btn {
color: #d9534f;
}
Relancez votre application (ou laissez le mode debug faire son travail) et rechargez la page pour voir les changements !
Conclusion
Félicitations ! Vous avez construit une application web To-Do List complète et fonctionnelle avec Flask et SQLAlchemy. Vous avez vu comment :
- Initialiser un projet Flask.
- Définir un modèle de données avec SQLAlchemy.
- Créer des templates HTML avec Jinja2.
- Gérer les requêtes GET et POST dans les routes.
- Effectuer les opérations CRUD (Create, Read, Update, Delete) sur votre base de données.
- Utiliser les redirections et
url_for
. - Ajouter un style basique avec CSS.
Ce projet est une excellente base pour comprendre les fondamentaux du développement web avec Python. À partir d’ici, vous pouvez explorer des fonctionnalités plus avancées : authentification utilisateur, catégories de tâches, dates d’échéance, utilisation de bases de données plus robustes comme PostgreSQL, ou même voir comment des outils comme Lovable AI pourraient vous aider à générer certaines parties de ce code plus rapidement grâce au Vibe Coding !
L’important est de continuer à pratiquer et à construire. Le développement web est un voyage passionnant !