Cours Flask Complet 🧪

Les 12 chapitres essentiels — micro-framework, routes, templates, API REST et déploiement

12
Chapitres
100+
Exemples de code
Flask 3+
Version
A1→C2
Niveaux

CHAPITRE 01

Introduction et installation

🧪 Hello World en 5 lignes
# app.py
from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello():
return 'Bonjour le monde !'

if __name__ == '__main__':
app.run(debug=True)

Flask est un micro-framework web Python. Créé par Armin Ronacher en 2010, Flask est minimaliste par design — il ne fournit que le routing et les templates (Jinja2). Tout le reste (ORM, auth, formulaires) est ajouté via des extensions. Utilisé par Netflix, Reddit, Uber, LinkedIn et Twitch.

⚙️ Installation
# Environnement virtuel
python -m venv venv
source venv/bin/activate

# Installer Flask
pip install flask

# Lancer
flask run # → http://127.0.0.1:5000
flask run –debug # Mode debug (hot reload)

# OU
python app.py

⚖️ Flask vs Django
AspectFlaskDjango
PhilosophieMicro, assemblez vous-mêmeBatteries included
ORMSQLAlchemy (extension)Intégré
AdminFlask-Admin (extension)Intégré (puissant)
AuthFlask-Login (extension)Intégré
CourbeRapide à apprendrePlus de concepts initiaux
Idéal pourAPI, microservices, prototypesApps complexes, CMS, SaaS
🧠 Quiz
Pourquoi « micro » dans micro-framework ?
« Micro » signifie que le noyau est simple et extensible, pas que Flask est limité. Flask fournit le routing, les templates et le serveur WSGI. Tout le reste (DB, auth, formulaires, mail) est ajouté à la carte via des extensions. Cela donne un contrôle total sur l'architecture.

CHAPITRE 02

Routes et vues

🔗 Décorateurs de routes
from flask import Flask, redirect, url_for, abort

app = Flask(__name__)

# Route simple
@app.route('/')
def index():
return 'Accueil'

# Route avec paramètre
@app.route('/users/')
def user_profile(user_id):
return f'Utilisateur '

# Plusieurs types de paramètres
@app.route('/post/') # string sans /
@app.route('/page/') # entier
@app.route('/price/') # décimal
@app.route('/path/') # string avec /

# Méthodes HTTP
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
return 'Formulaire soumis'
return 'Page de connexion'

# Redirections et erreurs
@app.route('/old')
def old_page():
return redirect(url_for('index')) # Rediriger vers 'index'

@app.route('/secret')
def secret():
abort(403) # Erreur 403 Forbidden

url_for() génère des URLs dynamiquement à partir du nom de la fonction. C'est plus sûr que d'écrire les URLs en dur — si vous changez le chemin, les liens se mettent à jour automatiquement.

CHAPITRE 03

Templates Jinja2

📝 Syntaxe Jinja2
# vue — passer des données au template
from flask import render_template

@app.route('/')
def index():
posts = [
,
,
] return render_template('index.html', posts=posts, title='Blog')





</span>Mon Site<span class="dc">





Par


Aucun article.

Bienvenue

Jinja2 est le même moteur de templates que Django (syntaxe quasi identique). Différences : dans les boucles (au lieu de ), (au lieu de ).

CHAPITRE 04

Requêtes et réponses

📨 L'objet request
from flask import request, jsonify

@app.route('/search')
def search():
query = request.args.get('q',  ») # ?q=python
page = request.args.get('page', 1, type=int) # ?page=2
return f'Recherche: , page '

@app.route('/login', methods=['POST'])
def login():
# Données de formulaire
email = request.form['email'] password = request.form['password'] return redirect(url_for('index'))

@app.route('/api/data', methods=['POST'])
def api_data():
# Données JSON
data = request.get_json()
name = data['name'] return jsonify(), 201

# Résumé des propriétés de request
request.method # « GET », « POST »
request.args # Query string (?key=value)
request.form # Données POST formulaire
request.get_json() # Corps JSON
request.headers # Headers HTTP
request.cookies # Cookies
request.files # Fichiers uploadés

CHAPITRE 05

Base de données (SQLAlchemy)

🗄️ Flask-SQLAlchemy
# pip install flask-sqlalchemy
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime, timezone

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db'
db = SQLAlchemy(app)

class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
posts = db.relationship('Post', backref='author', lazy=True)

def __repr__(self):
return f''

class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(200), nullable=False)
content = db.Column(db.Text)
created_at = db.Column(db.DateTime, default=lambda: datetime.now(timezone.utc))
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)

# CRUD avec SQLAlchemy

# Créer les tables
with app.app_context():
db.create_all()

# Create
user = User(username='alice', email='alice@mail.com')
db.session.add(user)
db.session.commit()

# Read
users = User.query.all()
user = User.query.get_or_404(1)
user = User.query.filter_by(username='alice').first()
posts = Post.query.filter(Post.title.contains('Flask')).all()
recent = Post.query.order_by(Post.created_at.desc()).limit(5).all()

# Update
user.email = 'newemail@mail.com'
db.session.commit()

# Delete
db.session.delete(user)
db.session.commit()

CHAPITRE 06

Migrations (Alembic)

📦 Flask-Migrate
# pip install flask-migrate
from flask_migrate import Migrate

migrate = Migrate(app, db)

# Initialiser les migrations (une seule fois)
flask db init

# Créer une migration après modification des modèles
flask db migrate -m « Add user table »

# Appliquer la migration
flask db upgrade

# Revenir en arrière
flask db downgrade

Ne jamais utiliser db.create_all() en production. C'est pratique pour le prototypage, mais ça ne gère pas les changements de schéma. Utilisez toujours Flask-Migrate (Alembic) pour traquer les modifications et migrer proprement.

CHAPITRE 07

Formulaires

📝 Flask-WTF
# pip install flask-wtf
from flask_wtf import FlaskForm
from wtforms import StringField, TextAreaField, SubmitField
from wtforms.validators import DataRequired, Length, Email

class PostForm(FlaskForm):
title = StringField('Titre', validators=[DataRequired(), Length(max=200)])
content = TextAreaField('Contenu', validators=[DataRequired()])
submit = SubmitField('Publier')

# N'oubliez pas la SECRET_KEY
app.config['SECRET_KEY'] = 'votre-clé-secrète'

# Vue
@app.route('/new', methods=['GET', 'POST'])
def create_post():
form = PostForm()
if form.validate_on_submit():
post = Post(title=form.title.data, content=form.content.data)
db.session.add(post)
db.session.commit()
flash('Article créé !', 'success')
return redirect(url_for('index'))
return render_template('create.html', form=form)




CHAPITRE 08

Authentification

🔐 Flask-Login
# pip install flask-login
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
from werkzeug.security import generate_password_hash, check_password_hash

login_manager = LoginManager(app)
login_manager.login_view = 'login'

# Le modèle User doit hériter de UserMixin
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True)
email = db.Column(db.String(120), unique=True)
password_hash = db.Column(db.String(256))

def set_password(self, password):
self.password_hash = generate_password_hash(password)

def check_password(self, password):
return check_password_hash(self.password_hash, password)

@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))

# Routes d'authentification
@app.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data).first()
if user and user.check_password(form.password.data):
login_user(user, remember=form.remember.data)
return redirect(url_for('index'))
flash('Email ou mot de passe invalide', 'error')
return render_template('login.html', form=form)

@app.route('/logout')
def logout():
logout_user()
return redirect(url_for('index'))

# Route protégée
@app.route('/dashboard')
@login_required
def dashboard():
return render_template('dashboard.html')

CHAPITRE 09

API REST

🔌 API avec Flask natif
from flask import jsonify, request

# GET /api/posts — Liste
@app.route('/api/posts')
def api_posts():
posts = Post.query.all()
return jsonify([ for p in posts])

# GET /api/posts/1 — Détail
@app.route('/api/posts/')
def api_post(id):
post = Post.query.get_or_404(id)
return jsonify()

# POST /api/posts — Créer
@app.route('/api/posts', methods=['POST'])
def api_create_post():
data = request.get_json()
post = Post(title=data['title'], content=data.get('content',  »))
db.session.add(post)
db.session.commit()
return jsonify(), 201

# DELETE /api/posts/1
@app.route('/api/posts/', methods=['DELETE'])
def api_delete_post(id):
post = Post.query.get_or_404(id)
db.session.delete(post)
db.session.commit()
return  », 204

Pour des API plus complexes, utilisez Flask-RESTX (génère une doc Swagger automatique) ou Flask-Smorest (validation avec Marshmallow). Pour les projets purement API sans templates, considérez FastAPI — plus moderne, typé, et avec une doc OpenAPI automatique.

CHAPITRE 10

Structure et Blueprints

📦 Blueprints — modulariser l'application
# blog/routes.py
from flask import Blueprint, render_template
from .models import Post

blog_bp = Blueprint('blog', __name__, url_prefix='/blog')

@blog_bp.route('/')
def post_list():
posts = Post.query.all()
return render_template('blog/list.html', posts=posts)

@blog_bp.route('/')
def post_detail(id):
post = Post.query.get_or_404(id)
return render_template('blog/detail.html', post=post)

# app/__init__.py — Application Factory
from flask import Flask
from .extensions import db, migrate, login_manager

def create_app():
app = Flask(__name__)
app.config.from_object('config.Config')

# Init extensions
db.init_app(app)
migrate.init_app(app, db)
login_manager.init_app(app)

# Enregistrer les blueprints
from .blog.routes import blog_bp
from .auth.routes import auth_bp
app.register_blueprint(blog_bp)
app.register_blueprint(auth_bp)

return app

L'Application Factory (create_app) est le pattern recommandé pour les projets Flask en production. Il permet de créer plusieurs instances (tests, dev, prod) avec des configurations différentes, et évite les imports circulaires.

🏗️ Structure recommandée
mon-app/
├── app/
│ ├── __init__.py # create_app()
│ ├── extensions.py # db, migrate, login_manager
│ ├── models.py # Tous les modèles
│ ├── blog/
│ │ ├── __init__.py
│ │ ├── routes.py # Blueprint blog
│ │ └── forms.py
│ ├── auth/
│ │ ├── __init__.py
│ │ ├── routes.py # Blueprint auth
│ │ └── forms.py
│ ├── templates/
│ │ ├── base.html
│ │ ├── blog/
│ │ └── auth/
│ └── static/
├── migrations/
├── config.py
├── requirements.txt
├── .env
└── run.py # create_app().run()

CHAPITRE 11

Déploiement

🚀 Production
# config.py
import os

class Config:
SECRET_KEY = os.environ.get('SECRET_KEY')
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL')
SQLALCHEMY_TRACK_MODIFICATIONS = False

class DevConfig(Config):
DEBUG = True
SQLALCHEMY_DATABASE_URI = 'sqlite:///dev.db'

class ProdConfig(Config):
DEBUG = False

# Gunicorn — serveur WSGI production
pip install gunicorn
gunicorn « app:create_app() » –bind 0.0.0.0:8000 –workers 4

# requirements.txt
pip freeze > requirements.txt

# Procfile (Heroku / Railway / Render)
web: gunicorn « app:create_app() »

PlateformeIdéal pour
RailwayGit push, PostgreSQL intégré, simple
RenderFree tier, auto-deploy depuis GitHub
Fly.ioDocker, edge deployment
PythonAnywhereHébergement Python spécialisé
AWS / DigitalOceanVPS, Nginx + Gunicorn

CHAPITRE 12

Bonnes pratiques

🧩 Extensions essentielles
BesoinExtension
ORMFlask-SQLAlchemy
MigrationsFlask-Migrate (Alembic)
AuthFlask-Login
FormulairesFlask-WTF (WTForms)
API RESTFlask-RESTX ou Flask-Smorest
CORSFlask-CORS
MailFlask-Mail
AdminFlask-Admin
CacheFlask-Caching
✅ Bonnes pratiques

✅ À FAIRE
• Application Factory (create_app)
• Blueprints pour modulariser
• Flask-Migrate pour les migrations
• Variables d'env (python-dotenv)
SECRET_KEY aléatoire et secrète
get_or_404 plutôt que try/except
• Gunicorn en production
• Tests avec pytest
flask-cors si API frontend séparé

❌ À ÉVITER
app.run(debug=True) en production
db.create_all() en production
• SECRET_KEY en dur dans le code
• Tout dans un seul fichier (> 200 lignes)
• Ignorer les migrations
• SQL brut sans paramètres (injection)
• Stocker les mots de passe en clair
* dans les imports
• Pas de gestion d'erreurs

🧠 Quiz
Flask vs FastAPI — quand choisir quoi ?
Flask = écosystème mature (templates, formulaires, sessions), rendu HTML, projets full-stack, communauté immense. FastAPI = API pures, async natif, typage automatique (Pydantic), doc OpenAPI auto, performances supérieures. Si vous faites un site web avec HTML → Flask. Si vous faites une API pure → FastAPI.
Qu'est-ce que l'Application Factory et pourquoi l'utiliser ?
C'est une fonction create_app() qui crée et configure l'instance Flask. Avantages : pas de variable globale app, possibilité de créer plusieurs instances (tests avec config différente), évite les imports circulaires, configuration par environnement (dev/prod).

Cours Flask Complet — Micro-framework, SQLAlchemy, API REST et déploiement

Référence : flask.palletsprojects.com | Flask-SQLAlchemy