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
Aspect Flask Django
Philosophie Micro, assemblez vous-même Batteries included
ORM SQLAlchemy (extension) Intégré
Admin Flask-Admin (extension) Intégré (puissant)
Auth Flask-Login (extension) Intégré
Courbe Rapide à apprendre Plus de concepts initiaux
Idéal pour API, microservices, prototypes Apps 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() »

Plateforme Idéal pour
Railway Git push, PostgreSQL intégré, simple
Render Free tier, auto-deploy depuis GitHub
Fly.io Docker, edge deployment
PythonAnywhere Hébergement Python spécialisé
AWS / DigitalOcean VPS, Nginx + Gunicorn

CHAPITRE 12

Bonnes pratiques

🧩 Extensions essentielles
Besoin Extension
ORM Flask-SQLAlchemy
Migrations Flask-Migrate (Alembic)
Auth Flask-Login
Formulaires Flask-WTF (WTForms)
API REST Flask-RESTX ou Flask-Smorest
CORS Flask-CORS
Mail Flask-Mail
Admin Flask-Admin
Cache Flask-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