Cours Flask Complet 🧪
Les 12 chapitres essentiels — micro-framework, routes, templates, API REST et déploiement
🏠 Hub Programmation
🐍 Python (prérequis)
🐍 Django
🎨 HTML & CSS
☁️ AWS Cloud Practitioner
Introduction et installation
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.
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
| 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 |
Routes et vues
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/
@app.route('/page/
@app.route('/price/
@app.route('/path/
# 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.
Templates Jinja2
from flask import render_template
@app.route('/')
def index():
posts = [
,
,
]
return render_template('index.html', posts=posts, title='Blog')
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 ).
Requêtes et réponses
@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
Base de données (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)
# 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()
Migrations (Alembic)
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.
Formulaires
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'
@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)
Authentification
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))
@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')
API REST
# 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/
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.
Structure et Blueprints
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)
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.
├── 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()
Déploiement
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
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 |
Bonnes pratiques
| 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 |
| Flask-Mail | |
| Admin | Flask-Admin |
| Cache | Flask-Caching |
✅ À 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
🏠 Hub Programmation
🐍 Cours Python
🐍 Cours Django
🎨 Cours HTML & CSS
☁️ AWS Cloud Practitioner
Cours Flask Complet — Micro-framework, SQLAlchemy, API REST et déploiement
Référence : flask.palletsprojects.com | Flask-SQLAlchemy
