0%
Flask : Créer des Applications Web Légères en Python

Flask : Créer des Applications Web Légères en Python

Découvrez Flask pour créer des applications web légères en Python : routes, templates Jinja2, formulaires, API JSON, blueprints et déploiement en production.

I

InSkillCoach

· min

Flask : Créer des Applications Web Légères en Python

Flask est un micro-framework web pour Python : il fournit le strict nécessaire pour construire une application web (routage, templates, gestion des requêtes) et vous laisse choisir le reste (base de données, authentification, formulaires). Cette philosophie minimaliste en fait un excellent point d’entrée pour apprendre le développement web en Python, tout en restant assez puissant pour des applications en production. Ce guide vous accompagne de l’installation jusqu’au déploiement.

Installation et application minimale

Créez un environnement virtuel, puis installez Flask :

# Création et activation de l'environnement virtuel
python -m venv venv

# Windows
venv\Scripts\activate
# macOS / Linux
source venv/bin/activate

# Installation
pip install flask

Une application Flask complète tient en quelques lignes. Créez un fichier app.py :

from flask import Flask

# Création de l'application
app = Flask(__name__)

# Association d'une URL à une fonction (une "vue")
@app.route('/')
def accueil():
    return "Bonjour, Flask !"

if __name__ == '__main__':
    # debug=True : rechargement automatique et messages d'erreur détaillés
    app.run(debug=True)

Lancez le serveur de développement :

python app.py
# * Running on http://127.0.0.1:5000

Ouvrez http://127.0.0.1:5000 dans votre navigateur : la page affiche « Bonjour, Flask ! ». N’utilisez jamais debug=True en production : le débogueur intégré permet d’exécuter du code arbitraire.

Routes et méthodes HTTP

Le décorateur @app.route associe une URL à une fonction. Par défaut, une route ne répond qu’aux requêtes GET ; le paramètre methods autorise d’autres verbes HTTP :

from flask import Flask, request

app = Flask(__name__)

@app.route('/contact', methods=['GET', 'POST'])
def contact():
    if request.method == 'POST':
        # Traitement de l'envoi du formulaire
        return "Message reçu"
    # Affichage du formulaire (GET)
    return "Formulaire de contact"
MéthodeUsage typique
GETLire une ressource, afficher une page
POSTCréer une ressource, soumettre un formulaire
PUTRemplacer une ressource existante
PATCHModifier partiellement une ressource
DELETESupprimer une ressource

Paramètres d’URL

Les segments variables d’une URL se déclarent entre chevrons et sont passés en argument à la fonction :

# Paramètre chaîne (par défaut)
@app.route('/utilisateur/<nom>')
def profil(nom):
    return f"Profil de {nom}"

# Paramètre entier : /article/42 fonctionne, /article/abc renvoie 404
@app.route('/article/<int:article_id>')
def article(article_id):
    return f"Article numéro {article_id}"

# Paramètre chemin : accepte les barres obliques
@app.route('/fichiers/<path:chemin>')
def fichier(chemin):
    return f"Chemin demandé : {chemin}"

Convertisseurs disponibles : string (défaut), int, float, path, uuid.

Les paramètres de requête (?tri=date&page=2) se lisent avec request.args :

@app.route('/recherche')
def recherche():
    terme = request.args.get('q', '')            # valeur par défaut : ''
    page = request.args.get('page', 1, type=int)  # conversion automatique
    return f"Recherche de « {terme} », page {page}"

Templates Jinja2

Retourner du HTML dans des chaînes Python devient vite illisible. Flask intègre le moteur de templates Jinja2 : les fichiers HTML vivent dans un dossier templates/.

Structure du projet :

mon_projet/
├── app.py
├── templates/
│   ├── base.html
│   └── index.html
└── static/
    └── style.css

Héritage de templates

Le template de base définit la structure commune ; les pages remplissent les blocs :

<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <title>{% block titre %}Mon Site{% endblock %}</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
    <nav>
        <a href="{{ url_for('accueil') }}">Accueil</a>
    </nav>
    <main>
        {% block contenu %}{% endblock %}
    </main>
</body>
</html>
<!-- templates/index.html -->
{% extends "base.html" %}

{% block titre %}Accueil - Mon Site{% endblock %}

{% block contenu %}
<h1>Bienvenue, {{ nom }} !</h1>

{% if articles %}
<ul>
    {% for article in articles %}
    <li>{{ article.titre }} ({{ article.date }})</li>
    {% endfor %}
</ul>
{% else %}
<p>Aucun article pour le moment.</p>
{% endif %}
{% endblock %}

Côté Python, render_template charge le template et lui transmet des variables :

from flask import render_template

@app.route('/')
def accueil():
    articles = [
        {"titre": "Premier article", "date": "2026-09-01"},
        {"titre": "Deuxième article", "date": "2026-09-10"},
    ]
    return render_template('index.html', nom="Alice", articles=articles)

Jinja2 échappe automatiquement le HTML des variables, ce qui protège contre les attaques XSS.

Fichiers statiques

Les feuilles de style, scripts et images vont dans le dossier static/. La fonction url_for génère leurs URLs :

<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<img src="{{ url_for('static', filename='images/logo.png') }}" alt="Logo">

Utilisez toujours url_for plutôt que des chemins en dur : les URLs restent correctes même si l’application est déployée sous un préfixe.

Formulaires et objet request

Les données d’un formulaire envoyé en POST se lisent avec request.form :

from flask import Flask, request, render_template

app = Flask(__name__)

@app.route('/inscription', methods=['GET', 'POST'])
def inscription():
    if request.method == 'POST':
        nom = request.form.get('nom', '').strip()
        email = request.form.get('email', '').strip()

        # Validation minimale
        if not nom or not email:
            return render_template('inscription.html',
                                   erreur="Tous les champs sont obligatoires")

        # Ici : enregistrement en base de données
        return f"Bienvenue, {nom} !"

    return render_template('inscription.html')
<!-- templates/inscription.html -->
{% extends "base.html" %}
{% block contenu %}
{% if erreur %}<p class="erreur">{{ erreur }}</p>{% endif %}
<form method="post">
    <input type="text" name="nom" placeholder="Votre nom">
    <input type="email" name="email" placeholder="Votre email">
    <button type="submit">S'inscrire</button>
</form>
{% endblock %}

Pour les projets réels, la bibliothèque Flask-WTF ajoute la validation et la protection CSRF.

Redirections et messages flash

Après un POST réussi, redirigez l’utilisateur (pattern « Post/Redirect/Get ») et affichez un message temporaire avec flash :

from flask import Flask, flash, redirect, url_for, render_template

app = Flask(__name__)
app.secret_key = "changez-moi-en-production"  # requis pour flash et les sessions

@app.route('/enregistrer', methods=['POST'])
def enregistrer():
    # ... traitement ...
    flash("Enregistrement réussi !", "succes")
    return redirect(url_for('accueil'))
<!-- Dans base.html, avant le bloc contenu -->
{% with messages = get_flashed_messages(with_categories=true) %}
    {% for categorie, message in messages %}
    <div class="flash flash-{{ categorie }}">{{ message }}</div>
    {% endfor %}
{% endwith %}

API JSON avec jsonify

Flask convient très bien aux API REST. La fonction jsonify sérialise un dictionnaire en réponse JSON avec le bon en-tête Content-Type :

from flask import Flask, jsonify, request

app = Flask(__name__)

TACHES = [
    {"id": 1, "titre": "Apprendre Flask", "faite": True},
    {"id": 2, "titre": "Créer une API", "faite": False},
]

@app.route('/api/taches', methods=['GET'])
def lister_taches():
    return jsonify(TACHES)

@app.route('/api/taches/<int:tache_id>', methods=['GET'])
def detail_tache(tache_id):
    tache = next((t for t in TACHES if t["id"] == tache_id), None)
    if tache is None:
        return jsonify({"erreur": "Tâche introuvable"}), 404
    return jsonify(tache)

@app.route('/api/taches', methods=['POST'])
def creer_tache():
    donnees = request.get_json()  # corps JSON de la requête
    if not donnees or "titre" not in donnees:
        return jsonify({"erreur": "Le champ 'titre' est requis"}), 400
    nouvelle = {"id": len(TACHES) + 1, "titre": donnees["titre"], "faite": False}
    TACHES.append(nouvelle)
    return jsonify(nouvelle), 201

Test rapide avec curl :

curl http://127.0.0.1:5000/api/taches
# [{"faite": true, "id": 1, "titre": "Apprendre Flask"}, ...]

curl -X POST http://127.0.0.1:5000/api/taches \
     -H "Content-Type: application/json" \
     -d '{"titre": "Déployer"}'
# {"faite": false, "id": 3, "titre": "Déployer"}

Structurer avec les blueprints

Quand l’application grossit, un fichier unique devient ingérable. Les blueprints regroupent les routes par domaine fonctionnel :

# blog/routes.py
from flask import Blueprint, render_template

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

@blog_bp.route('/')
def liste_articles():
    return render_template('blog/liste.html')

@blog_bp.route('/<int:article_id>')
def detail(article_id):
    return render_template('blog/detail.html', article_id=article_id)
# app.py
from flask import Flask
from blog.routes import blog_bp

app = Flask(__name__)
app.register_blueprint(blog_bp)
# Les routes /blog/ et /blog/<id> sont maintenant actives

Structure typique d’un projet organisé :

mon_projet/
├── app.py
├── blog/
│   └── routes.py
├── auth/
│   └── routes.py
├── templates/
└── static/

Configuration et variables d’environnement

Ne mettez jamais de secrets (clés, mots de passe de base de données) dans le code. Chargez-les depuis l’environnement :

import os
from flask import Flask

app = Flask(__name__)

app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'dev-uniquement')
app.config['DATABASE_URL'] = os.environ.get('DATABASE_URL')
app.config['DEBUG'] = os.environ.get('FLASK_DEBUG', '0') == '1'

En développement, le package python-dotenv charge automatiquement un fichier .env (à exclure du dépôt Git via .gitignore) :

# .env
SECRET_KEY=une-longue-chaine-aleatoire
DATABASE_URL=postgresql://user:pass@localhost/mabase
FLASK_DEBUG=1

Déploiement : gunicorn et nginx

Le serveur intégré de Flask est mono-processus et non sécurisé : il est réservé au développement. En production, utilisez un serveur WSGI comme Gunicorn derrière un serveur web nginx :

pip install gunicorn

# 4 processus de travail, à l'écoute sur le port 8000
gunicorn --workers 4 --bind 127.0.0.1:8000 app:app

nginx sert de proxy inverse : il reçoit le trafic HTTPS, sert les fichiers statiques directement et transmet le reste à Gunicorn :

server {
    listen 80;
    server_name exemple.com;

    location /static/ {
        alias /var/www/mon_projet/static/;
    }

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Des plateformes comme Render, Railway ou Fly.io automatisent ce montage si vous ne souhaitez pas gérer de serveur.

Flask vs Django

CritèreFlaskDjango
PhilosophieMicro-framework, minimalFramework complet, « batteries incluses »
Courbe d’apprentissageDouce, on apprend au fil des besoinsPlus raide, beaucoup de concepts d’emblée
ORMAu choix (SQLAlchemy le plus souvent)ORM Django intégré
AdministrationÀ construire ou via extensionInterface d’admin générée automatiquement
AuthentificationExtension (Flask-Login)Intégrée
Structure de projetLibreImposée et conventionnelle
Idéal pourAPI, microservices, petits sites, prototypesApplications complètes avec back-office

Les deux frameworks sont matures et utilisés en production à grande échelle. Choisissez Flask pour sa légèreté et sa flexibilité, Django quand vous voulez un maximum de fonctionnalités prêtes à l’emploi.

Bonnes pratiques

  • Utilisez toujours un environnement virtuel et un fichier requirements.txt (pip freeze > requirements.txt).
  • Désactivez le mode debug en production : c’est une faille de sécurité majeure.
  • Générez les URLs avec url_for plutôt que de les écrire en dur.
  • Stockez les secrets dans des variables d’environnement, jamais dans le code source.
  • Structurez avec des blueprints dès que l’application dépasse quelques routes.
  • Retournez les bons codes HTTP dans vos API : 201 pour une création, 400 pour une requête invalide, 404 pour une ressource absente.
  • Validez toutes les entrées utilisateur côté serveur, même si le formulaire les valide déjà côté client.

Conclusion

Flask illustre parfaitement la philosophie Python : simple pour démarrer, extensible pour grandir. Vous savez maintenant créer des routes, générer des pages avec Jinja2, traiter des formulaires, exposer une API JSON, organiser votre code avec des blueprints et déployer avec Gunicorn et nginx. La suite logique : brancher une base de données avec Flask-SQLAlchemy, gérer l’authentification avec Flask-Login et sécuriser vos formulaires avec Flask-WTF.

Pour approfondir, consultez la documentation officielle de Flask, remarquablement claire et complète.

InSkillCoach

À propos de InSkillCoach

Expert en formation et technologies

Coach spécialisé dans les technologies avancées et l'IA, porté par GNeurone Inc.

Certifications:

  • AWS Certified Solutions Architect – Professional
  • Certifications Google Cloud
  • Microsoft Certified: DevOps Engineer Expert
  • Certified Kubernetes Administrator (CKA)
  • CompTIA Security+
1.1k
128

Commentaires

Les commentaires sont alimentés par GitHub Discussions

Connectez-vous avec GitHub pour participer à la discussion

Lien copié !