Mémo Python Web

Guide moderne du développement Back-end : Micro-frameworks, APIs robustes, Sécurité et Déploiement Cloud.

Pourquoi Python pour le Web ?

Si JavaScript règne sur le Frontend, Python est le roi du Backend moderne, notamment grâce à l'essor de l'IA. Des géants comme Instagram, Spotify, Netflix et Uber utilisent Python pour gérer des millions de requêtes.

Avec l'arrivée de l'asynchrone (FastAPI) et la maturité de Django, Python offre l'équilibre parfait entre rapidité de développement et performance à l'échelle.

00

Le Python dans le Web

Mise en situation et architectures

📖 Comprendre le rôle de Python sur le Web

Python n'est pas exécuté nativement par les navigateurs (contrairement à JavaScript). Son rôle se situe côté Serveur (Back-end).

Il reçoit les requêtes HTTP, traite la logique métier, interagit avec la base de données, et renvoie une réponse (HTML ou JSON).

Pour communiquer avec les serveurs web (comme Nginx ou Apache), Python utilise des standards : WSGI (synchrone, classique) ou ASGI (asynchrone, moderne).

🧭 Idée clé — Le navigateur gère l'interface, Python gère la logique, les données et la sécurité. Le dialogue se fait par HTTP.

Rôle principal

Recevoir une requête, exécuter une logique métier, répondre en HTML ou JSON.

Entrées

URL, paramètres, formulaires, cookies, headers, payload JSON.

Sorties

Page HTML, JSON d'API, redirection, fichier téléchargé.

État

Sessions, tokens JWT, cache, base de données.

Contraintes

Latence, sécurité, montée en charge, observabilité.

Objectif

API fiable, performante, maintenable et documentée.

Flux d'une requête Web (vue simplifiée)
Navigateur
   │ HTTP (GET/POST)
   ▼
Serveur Web (Nginx/Apache)
   │ reverse proxy
   ▼
Serveur d'application (Gunicorn/Uvicorn)
   │ WSGI ou ASGI
   ▼
Python (routes, logique, ORM)
   │
   ▼
Base de données / Cache / Services externes
   │
   ▼
Réponse (HTML/JSON)

Rendu serveur (SSR)

Python compose la page HTML et renvoie une vue prête à afficher. Idéal pour les sites orientés contenu et SEO.

API JSON + Front

Python expose des endpoints, et un front (React/Vue/Svelte) consomme l'API. Idéal pour les apps riches et interactives.

⚙️ WSGI vs ASGI en pratique

WSGI est le standard historique pour des apps synchrones. Simple, robuste, très répandu en production (Gunicorn + Flask/Django).

ASGI étend WSGI pour l'asynchrone, les WebSockets et les tâches en streaming. Indispensable pour le temps réel (Uvicorn + FastAPI/Django async).

Conseil — Si votre application n'a pas de temps réel, WSGI reste un choix pragmatique. Pour chat, live, notifications, privilégiez ASGI.
🧱 Architecture type d'une app Python Web

Une app moderne est découpée en couches pour rester lisible et testable :

RoutesServicesAccès donnéesInfrastructure.

Ce découpage simplifie la maintenance, évite le code spaghetti et facilite la montée en charge.

Astuce — Une route doit orchestrer, pas porter toute la logique métier.

Questions à se poser

Combien d'utilisateurs ? Quels temps de réponse ? Temps réel ? API publique ? Équipe ?

Choisir un style

Monolithe simple au début, micro-services quand les besoins et l'équipe grandissent.

⚠️ À ne pas confondre — Nginx/Apache servent les fichiers et distribuent les requêtes. Gunicorn/Uvicorn exécutent le code Python.

Flask

Micro-framework

Minimaliste, flexible. Vous choisissez vos outils. Idéal pour débuter ou pour des micro-services.

Django

Batteries included

Tout inclus (ORM, Admin, Auth). Robuste et sécurisé. Idéal pour des projets complexes rapides.

FastAPI

Haute performance

Moderne, asynchrone, typé. Génère automatiquement la doc API. Le nouveau standard.

🎯 Testez vos connaissances

Prêt pour le défi ?

Mesurez votre niveau sur l'ensemble de l'écosystème Python Web (Flask, Django, FastAPI, Docker, etc.).

🚀 Lancer le Quiz Global
01

Environnement & Tooling Python Web

Isolation, dépendances, workflow d’équipe

🧭 Mise en contexte — Un projet web vit longtemps. Sans isolation et sans versionnage clair des dépendances, les bugs deviennent imprévisibles et la collaboration se fragilise.
📖 Comprendre l'isolation des environnements

En développement web, il est CRITIQUE d'isoler les dépendances de chaque projet pour éviter les conflits de versions.

On utilise un Virtual Environment (venv). C'est un dossier local qui contient une copie de l'interpréteur Python et des bibliothèques installées.

On évite ainsi qu'une mise à jour globale casse un autre projet.

venv/

Environnement isolé local au projet.

requirements.txt

Liste figée des dépendances installées.

pyproject.toml

Configuration moderne (build, deps, outils).

.env

Variables sensibles et configs locales.

pip cache

Accélère les installs en CI/CD.

Python version

La base du projet, à figer clairement.

Setup Projet (classique)
# 1. Créer le dossier projet
mkdir mon_projet ; cd mon_projet

# 2. Créer l'environnement virtuel
python -m venv venv

# 3. Activer l'environnement
# Windows : venv\Scripts\activate
# Mac/Linux : source venv/bin/activate

# 4. Installer les dépendances
pip install flask sqlalchemy python-dotenv

# 5. Figer les versions
pip freeze > requirements.txt

Workflow minimal d’équipe

Cloner le repo → créer venv → installer deps → lancer l’app → exécuter les tests.

Piège courant

Oublier d’activer le venv et installer les packages globalement.

Cycle de vie des dépendances
Dév local
  │ pip install
  ▼
requirements.txt (ou pyproject.toml)
  │
  ▼
CI/CD installe les mêmes versions
  │
  ▼
Production stable
🧪 Nouveaux concepts clés

Lockfile : fige exactement les versions pour un build reproductible.

Build backend : manière de compiler/packager une lib (PEP 517).

12-factor config : séparer la config du code via variables d’env.

Astuce — Évitez les dépendances non versionnées pour prévenir les surprises.
🔁 Alternatives d’outillage

virtualenv

Plus ancien que venv, encore très utilisé.

Pipenv

Environnements + lockfile, orienté simplicité.

Poetry

Gestion moderne des deps via pyproject.toml.

uv

Gestionnaire ultra rapide, workflow moderne.

conda

Très utilisé en data science, plus lourd.

pip-tools

Compile un requirements.txt verrouillé.

⚠️ Sécurité — Ne stockez jamais de secrets dans le code. Utilisez des variables d’environnement et un .env local ignoré par Git.
✅ Checklist “projet prêt à partager”

— Version Python précisée.

— Dépendances figées (requirements ou lockfile).

— .env.example fourni.

— Commandes de lancement documentées.

— Tests basiques exécutables.

🎓 Mini-atelier guidé

Objectif : créer un squelette web propre en 10 minutes.

Étape 1 — Créez un venv et installez Flask.

Étape 2 — Lancez une route “/health” qui renvoie “OK”.

Étape 3 — Figez vos dépendances et partagez le projet.

Question — Si un collègue clone le repo, quelles étapes doit-il exécuter pour obtenir le même résultat ?
02

Flask : Fondations & Patterns Pro

Micro-framework WSGI flexible et extensible

Flask est dit "Micro" non pas parce qu'il manque de puissance, mais parce qu'il ne prend pas de décisions à votre place (pas d'ORM imposé, pas de système de login par défaut). Il repose sur deux piliers : Werkzeug (WSGI) et Jinja2 (Templating).

1. Architecture & Cycle de Vie

Dans une application Flask, le flux suit une route précise de la requête à la réponse :

graph LR A["Navigateur"] -->|Requête HTTP| B["Werkzeug / WSGI"] B --> C["Flask App"] C --> D{"Route / URL"} D -->|Blueprint A| E["Logic"] D -->|Blueprint B| F["Logic"] E -->|Jinja2| G["HTML / JSON"] F -->|Jinja2| G G -->|Réponse HTTP| B B --> A

2. Les Contextes (La "Magie" de Flask)

Flask utilise des Proxies pour accéder à des données globales sans polluer les signatures de fonctions :

  • request : Accès aux données de la requête (args, form, json).
  • session : Stockage sécurisé côté client (cookies signés).
  • g : Objet temporaire pour une seule requête (ex: stocker la connexion DB).
  • current_app : Accès à l'instance de l'application active.

3. Structure "Industrielle" (Factory & Blueprints)

Pour un projet sérieux, on évite le fichier unique. On utilise le Application Factory Pattern.

Arborescence Type
/mon_projet
├── /app                # Package principal
│   ├── __init__.py     # Initialisation (Factory)
│   ├── models.py       # Définition des DB
│   ├── /auth           # Module d'authentification
│   │   ├── routes.py
│   │   └── forms.py
│   └── /api            # Module API REST
│       └── routes.py
├── config.py           # Configurations (Dev, Prod, Test)
└── requirements.txt
Exemple __init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

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

    db.init_app(app)

    # Enregistrement des Blueprints
    from .auth import auth_bp
    app.register_blueprint(auth_bp, url_prefix='/auth')

    return app
💡 Pourquoi les Blueprints ? Ils permettent de découpler votre code. Vous pouvez tester le module "Auth" indépendamment du module "API", et même les réutiliser dans d'autres projets Flask.

4. Jinja2 : Plus qu'un simple moteur

Héritage

Définissez un base.html et "étendez-le" dans vos pages avec {% extends "base.html" %}.

Filtres

Transformez vos données dans l'HTML : {{ date | format_date }} ou {{ texte | truncate(50) }}.

Macros

Créez des composants réutilisables (fonctions HTML) pour vos formulaires ou boutons.

➕ Extensions Essentielles
Extension Rôle
Flask-SQLAlchemy Intégration simplifiée de l'ORM SQLAlchemy.
Flask-Migrate Gestion des migrations de base de données (Alembic).
Flask-Login Gestion des sessions et authentification utilisateur.
Flask-WTF Formulaires sécurisés avec protection CSRF.
Flask-JWT-Extended Authentification par jeton (JSON Web Tokens).
03

SQLAlchemy : ORM & Maîtrise des Données

Le standard industriel pour la persistance en Python

SQLAlchemy n'est pas qu'un simple ORM (Object-Relational Mapper) ; c'est une boîte à outils complète qui sépare l'Abstraction SQL de la Gestion des Objets. Il permet de manipuler vos bases de données comme des objets Python tout en conservant la puissance du SQL brut si nécessaire.

1. Le Concept de "Session"

La Session agit comme une Unité de Travail (Unit of Work). Elle suit tous les changements apportés à vos objets avant de les synchroniser avec la base.

graph TD A[Objets Python] -->|add / delete| B[Session / Unit of Work] B -->|flush| C{Tampon de Base} C -->|commit| D[(Base de Données)] D -->|query| B B -->|refresh| A

2. Définition d'un Modèle Pro

Un modèle bien défini utilise des types précis et des contraintes pour garantir l'intégrité des données.

models.py
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime

db = SQLAlchemy()

class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100), nullable=False)
    content = db.Column(db.Text, nullable=False)
    date = db.Column(db.DateTime, default=datetime.utcnow)
    
    # Relation Many-to-One
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))

3. Relations & Chargement (Loading)

One-to-Many

Un utilisateur a plusieurs articles. Utilisation de db.relationship() pour naviguer facilement entre les objets.

Eager Loading

joinedload() permet de récupérer les données liées en une seule requête SQL (évite le problème N+1).

Lazy Loading

Par défaut, SQLAlchemy ne charge les relations que lorsqu'on y accède, économisant de la mémoire sur les objets simples.

4. Le Workflow CRUD

Manipulations courantes
# CREATE
user = User(username='alice')
db.session.add(user)
db.session.commit()

# READ
users = User.query.all()
bob = User.query.filter_by(username='bob').first()

# UPDATE
bob.email = 'bob@new.com'
db.session.commit()

# DELETE
db.session.delete(user)
db.session.commit()
💡 Tip: Alembic & Migrations

En production, on n'utilise jamais db.create_all(). On utilise Alembic (via Flask-Migrate) pour versionner les changements de schéma de la base de données.

➕ Alternatives & Évolutions

SQLModel

Une nouvelle lib basée sur SQLAlchemy et Pydantic, idéale pour FastAPI car elle fusionne modèles DB et schémas de validation.

Peewee

Un ORM beaucoup plus léger et simple, parfait pour de petits projets ou scripts où SQLAlchemy serait surdimensionné.

04

Django : Le Framework des Perfectionnistes

"Batteries included" - Productivité maximale et sécurité native

Django est un framework web de haut niveau qui encourage un développement rapide et une conception propre. Contrairement à Flask, il arrive avec tout le nécessaire : ORM, Auth, Admin, Migrations, et protection contre les failles (SQLi, XSS, CSRF) activées par défaut.

1. Le Pattern MVT

Django utilise une variante du MVC appelée Model-View-Template. La logique de contrôle est gérée par le framework lui-même.

graph TD URL[URL Conf / Router] --> V[View / Logic] V --> M[Model / Database] M --> V V --> T[Template / HTML] T --> V V --> Res[Réponse HTTP]

2. L'Interface d'Administration

C'est l'un des "tueurs" de Django : une interface de gestion de contenu générée automatiquement à partir de vos modèles.

admin.py
from django.contrib import admin
from .models import Product

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    list_display = ('name', 'price', 'stock')
    search_fields = ('name',)
    list_filter = ('category',)

3. Puissance des Class-Based Views (CBV)

Pour les tâches courantes (lister, afficher, créer), Django propose des classes génériques qui réduisent drastiquement le code.

views.py
from django.views.generic import ListView
from .models import Post

class PostListView(ListView):
    model = Post
    template_name = 'blog/list.html'
    paginate_by = 10  # Pagination automatique !

🚀 Pourquoi choisir Django ?

  • Structure stricte : Idéal pour les grosses équipes.
  • Écosystème immense : Des milliers de "apps" réutilisables.
  • Sécurité par défaut : Moins de risques d'erreurs humaines.
  • Migrations intégrées : Gestion native du schéma DB.

4. Django REST Framework (DRF)

Si vous voulez créer des APIs, DRF est le standard absolu pour Django. Il ajoute des sérialiseurs puissants et une interface de test d'API.

serializers.py
from rest_framework import serializers
from .models import User

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['id', 'username', 'email']
➕ Concepts Clés & Alternatives

Middlewares

Des composants qui interceptent chaque requête/réponse pour gérer l'auth, la compression, ou le cache globalement.

Signals

Permettent à des applications découplées d'être notifiées quand des actions se produisent (ex: créer un profil après l'inscription).

⚠️ Attention : Django a une courbe d'apprentissage plus raide que Flask par sa rigidité et sa taille. Il peut sembler "trop lourd" pour de simples microservices ou APIs légères.
05

FastAPI : La Performance Moderne

Framework ASGI ultra-rapide basé sur les types Python 3.10+

FastAPI est le framework qui a révolutionné le backend Python récent. Il repose sur deux piliers : Starlette (pour la partie web asynchrone) et Pydantic (pour la validation de données ultra-rapide). Sa signature ? L'utilisation massive des Type Hints de Python pour générer automatiquement de la documentation et valider les requêtes.

1. ASGI vs WSGI : L'Asynchronisme

Contrairement à Flask (WSGI), FastAPI est ASGI (Asynchronous Server Gateway Interface). Il peut gérer des milliers de connexions simultanément sans bloquer le serveur.

graph TD subgraph "WSGI (Synchrone)" W1[Req 1] --> S1[Occupé] W2[Req 2] --> S1 end subgraph "ASGI (Asynchrone)" A1[Req 1] --> EventLoop A2[Req 2] --> EventLoop EventLoop --> S2[Gère tout en parallèle] end

2. Validation & Pydantic

Fini les erreurs de type KeyError. FastAPI convertit le JSON entrant en vrais objets Python typés.

schemas.py
from pydantic import BaseModel, Field

class Item(BaseModel):
    name: str
    price: float = Field(gt=0, description="Doit être positif")
    is_offer: bool | None = None

@app.post("/items/")
async def create_item(item: Item):
    return item  # Auto-sérialisation JSON

3. Injection de Dépendances (Depends)

FastAPI possède un système puissant pour gérer les pré-requis (Auth, DB, Logging) de manière modulaire.

main.py
from fastapi import Depends, Header

async def get_token(x_token: str = Header()):
    if x_token != "fake-secret":
        raise HTTPException(status_code=400)
    return x_token

@app.get("/secure-data/")
async def read_data(token: str = Depends(get_token)):
    return {"auth": True}

4. Auto-Documentation Magique

Sans aucun code supplémentaire, FastAPI génère deux documentations interactives complètes :

Swagger UI (/docs)

Permet de tester vos APIs directement depuis le navigateur avec un bouton "Try it out".

ReDoc (/redoc)

Une version plus structurée et design, idéale pour la documentation technique partagée avec d'autres équipes.

💡 Pourquoi l'utiliser ? Si vous construisez une API à haute performance (Machine Learning, IoT, Real-time), FastAPI est le choix numéro 1. Pour les sites avec beaucoup de templates HTML côté serveur, Flask ou Django restent préférables.
➕ Concepts Avancés
Fonctionnalité Description
Background Tasks Lancer des fonctions après avoir renvoyé une réponse (ex: envoi d'email).
FastAPI Lifespan Gérer les événements de démarrage et d'extinction (connexions DB).
Middleware Gérer les CORS, Gzip, ou ajouter des headers personnalisés.
Path & Query Params Validation stricte des paramètres d'URL via Query et Path.
06

API REST : Architectures & Standards

Concevoir des interfaces robustes, prévisibles et scalables

Le style architectural REST (Representational State Transfer) n'est pas un protocole, mais un ensemble de contraintes. Une API "RESTful" utilise les standards du web (HTTP, URL, JSON) pour manipuler des ressources de manière sans état (stateless).

1. Les 6 Contraintes REST

  • 👤 Client-Serveur : Séparation des préoccupations (UI vs Data).
  • ☁️ Stateless : Chaque requête doit contenir toute l'info nécessaire.
  • 💾 Cacheable : Les réponses doivent indiquer si elles sont réutilisables.
  • 🧱 Layered System : Le client ne sait pas s'il parle au serveur final.
  • 🌐 Interface Uniforme : Utilisation cohérente des URI et méthodes.
  • 📜 Code on Demand (Optionnel) : Envoi de scripts exécutables.

2. Cycle Requête / Réponse

sequenceDiagram participant C as Client participant S as API REST C->>S: METHOD /resource (JSON/Headers) Note over S: Auth -> Validation -> Business Logic S-->>C: HTTP STATUS CODE + Body (JSON)

3. Méthodes HTTP & Idempotence

Méthode Action Idempotent ? Usage
GET 🔍 Récupérer ✅ Oui Récupérer une liste ou un élément (Lecture seule).
POST ➕ Créer ❌ Non Créer une nouvelle ressource (N+1 exécutions = N+1 créations).
PUT 🔄 Remplacer ✅ Oui Remplacer entièrement une ressource existante.
PATCH 📝 Modifier ❌ Non Modification partielle d'une ressource (souvent asynchrone).
DELETE 🗑️ Supprimer ✅ Oui Supprimer une ressource (2 exécutions = même résultat final).

4. Les Points de Douleur & Alternatives

Over-fetching

REST renvoie souvent trop de données. GraphQL permet au client de choisir exactement ses champs.

Under-fetching

REST nécessite souvent plusieurs appels d'API. HATEOAS (liens hypermédias) aide à la navigation.

Performance

Pour des microservices ultra-rapides, gRPC (Protocol Buffers) est souvent plus efficace que le JSON/REST.

5. Codes de Statut (Résumé Quick-Ref)

Familles de codes
  • 🟢 2xx (Success) : 200 (OK), 201 (Created), 204 (No Content).
  • 🟡 3xx (Redirection) : 301 (Moved Permanently), 304 (Not Modified).
  • 🟠 4xx (Client Error) : 400 (Bad Req), 401 (Unauthorized), 403 (Forbidden), 404 (Not Found).
  • 🔴 5xx (Server Error) : 500 (Internal Error), 502 (Bad Gateway), 503 (Service Unavailable).
💡 Richardson Maturity Model

Niveau 0 : HTTP pur. Niveau 1 : Ressources (URI). Niveau 2 : Verbes HTTP. Niveau 3 : HATEOAS (Le Graal du REST).

➕ Bonnes Pratiques JSON
Exemple JSON:API
{
  "data": {
    "type": "articles",
    "id": "1",
    "attributes": { "title": "REST is Life" },
    "links": { "self": "/articles/1" }
  }
}
  • Utilisez snake_case ou camelCase de manière cohérente.
  • Évitez les tableaux à la racine (sécurité).
  • Incluez toujours un champ id.
  • Gérez l'UTF-8 par défaut.
07

Sécurité & Auth : Protéger son Backend

Hashing, JWT, CORS et Architecture Défensive

La sécurité n'est pas une "feature" qu'on ajoute à la fin, c'est une fondation. En Python Web, cela signifie gérer correctement l'identité (Authentification) et les droits (Autorisation), tout en se protégeant contre les menaces automatisées.

1. Hashing : La Règle du "Slow"

On ne stocke JAMAIS de mot de passe. On stocke un Hash unique. Plus l'algorithme est "lent" (coûteux en CPU/Mémoire), plus il résiste aux attaques par force brute.

Argon2 (Recommandé)
from passlib.context import CryptContext

pwd_context = CryptContext(schemes=["argon2", "bcrypt"])

# Inscription
hash = pwd_context.hash("mypassword")

# Vérification
is_ok = pwd_context.verify("mypassword", hash)

2. JWT : Authentification Sans État

Le JSON Web Token permet d'identifier un utilisateur sans interroger la DB à chaque requête. Le secret côté serveur garantit l'intégrité.

sequenceDiagram Client->>Server: Login (User/Pass) Server-->>Client: JWT (Signé avec SECRET) Note right of Client: Stocké en mémoire ou Cookie Client->>Server: Requête (Headers: Bearer JWT) Server->>Server: Vérifie Signature Server-->>Client: Données

3. L'Arsenal de Défense (OWASP Tips)

CORS

Configurez les Cross-Origin Resource Sharing pour n'autoriser que vos domaines front-end de confiance.

Injection

Utilisez toujours les ORM (SQLAlchemy, Django) pour échapper automatiquement les paramètres et éviter les injections SQL.

Rate Limiting

Limitez le nombre de requêtes par IP (ex: via Flask-Limiter) pour contrer le DoS ou le brute-force.

4. Mise en Pratique : Vérification de Token

Logic de Décodage
from jose import jwt, JWTError

def validate_access(token: str):
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
        user_id = payload.get("sub")
        if user_id is None:
            raise HTTPException(status_code=401)
        return user_id
    except JWTError:
        raise HTTPException(status_code=401, detail="Invalid Token")
🛑 Erreur Critique : Ne jamais placer de données sensibles (mots de passe, numéros CB) dans le payload d'un JWT. Le JWT est lisible par n'importe qui (Base64), il n'est que signé, pas chiffré.
➕ Approfondissement : RBAC vs ABAC

RBAC (Role-Based)

Permission basée sur le titre (ex: admin, éditeur, utilisateur). Simple et efficace pour la plupart des apps.

ABAC (Attribute-Based)

Permission complexe basée sur des attributs (ex: "Peut éditer si l'article lui appartient ET s'il est avant 18h").

08

Async & WebSockets : Le Temps Réel

Du parallélisme avec AsyncIO à la communication Full-Duplex

Le web moderne ne se limite plus au cycle "Requête / Réponse". Avec l'asynchronisme (asyncio) et les WebSockets, Python peut gérer des flux de données en temps réel (chats, dashboards live, jeux) sans bloquer ses ressources systèmes.

1. L'Event Loop & asyncio

Au lieu de créer un thread par utilisateur (coûteux), Python utilise une Boucle d'Événements. Quand une tâche attend (ex: lecture DB), le serveur passe instantanément à la suivante.

graph LR Loop["Event Loop"] --> T1["Tâche 1: I/O Wait"] Loop --> T2["Tâche 2: Calcul"] T1 -.->|Pause / yield| Loop Loop --> T3["Tâche 3: Response"]

2. WebSockets vs HTTP

Alors que l'HTTP ferme la connexion après chaque réponse, le WebSocket maintient un tunnel ouvert et bidirectionnel.

sequenceDiagram participant C as Client participant S as Serveur C->>S: Upgrade Request (HTTP) S-->>C: 101 Switching Protocols Note over C,S: Connexion Persistante (TCP) C->>S: Data (JSON/Binary) S->>C: Data (Push temps réel) C->>S: Data

3. Implémentation avec FastAPI

websocket_server.py
from fastapi import FastAPI, WebSocket

app = FastAPI()

@app.websocket("/ws/{client_id}")
async def websocket_endpoint(websocket: WebSocket, client_id: int):
    await websocket.accept()
    try:
        while True:
            data = await websocket.receive_text()
            # Push de données au client sans qu'il demande
            await websocket.send_text(f"Message reçu : {data}")
    except Exception:
        print("Client déconnecté")

4. Cas d'Usage & Écosystème

📡 Broadcasting

Envoyer un message à tous les utilisateurs connectés simultanément (ex: notification système).

⏱️ Basse Latence

Idéal pour le trading ou les collaborations en direct (type Google Docs) où chaque ms compte.

🔌 Alternatives

Socket.io (couche au-dessus de WS pour la reconnexion auto) ou Server-Sent Events (SSE) pour du push unidirectionnel.

⚠️ Note sur le Déploiement

Les WebSockets nécessitent un serveur compatible ASGI (comme Uvicorn ou Daphne) et une configuration spécifique de Nginx (proxy_set_header Upgrade $http_upgrade;) pour ne pas couper la connexion.

➕ Syntaxe Moderne : async with & await
HTTP Client Asynchrone (Hittpx)
import httpx
import asyncio

async def fetch_data():
    async with httpx.AsyncClient() as client:
        resp = await client.get("https://api.example.com")
        return resp.json()

# Lancer plusieurs requêtes en parallèle
results = await asyncio.gather(fetch_data(), fetch_data())
09

Background Tasks : Déléguer pour la Performance

Optimiser l'expérience utilisateur en déchargeant les opérations lourdes

Dans une application web, la règle d'or est la réactivité. Si une action prend plus de 200ms (envoi d'email, génération de PDF, calcul IA), elle ne doit pas bloquer la requête HTTP. La solution : le pattern Producteur-Consommateur.

1. Architecture Distribuée

L'application (Producteur) envoie un message dans une file d'attente (Broker). Un processus séparé (Worker) récupère ce message et l'exécute en arrière-plan.

graph LR App["App Web"] -->|1. Envoie Tâche| B[("Broker: Redis / RMQ")] B -->|2. Pull| W["Worker: Celery"] W -->|3. Exécute| Job["Email / Image / PDF"] Job -.->|4. Stocke Résultat| DB[("Base de Données / Cache")]

2. Stratégies de Délégation

⚡ Solution Légère

FastAPI BackgroundTasks : Exécution dans le même processus après la réponse. Simple, idéal pour les emails rapides.

🏗️ Solution Robuste

Celery / Dramatiq : Processus isolés, gestion des retries, monitoring, scheduling (Crontab).

3. Comparatif Technique

In-Process (FastAPI)
from fastapi import BackgroundTasks

@app.post("/report")
async def generate_report(bg: BackgroundTasks):
    # S'exécute APRES le return
    bg.add_task(big_calculation)
    return {"status": "En cours"}
Distributed (Celery)
from celery import Celery

app = Celery(broker="redis://...")

@app.task
def heavy_task(x):
    return x ** 2

# Appel immédiat, réponse non bloquée
heavy_task.delay(10)

4. Monitoring & Fiabilité

Plus vous avez de tâches de fond, plus vous avez besoin de visibilité sur leur état (échec, succès, temps d'exécution).

Flower 🌸

L'interface web standard pour monitorer Celery en temps réel (files, workers, taux d'erreur).

Retries 🔄

Celery permet de re-tenter automatiquement une tâche si une API tierce est temporairement indisponible.

Beat ⏲️

Le planificateur de Celery pour exécuter des tâches à intervalle régulier (ex: stats tous les lundis à 8h).

💡 Tip: Redis vs RabbitMQ

Redis est parfait pour débuter (simple, rapide, souvent déjà là pour le cache). RabbitMQ est plus robuste pour des millions de messages avec des routages complexes.

➕ Alternatives Modernes

Dramatiq

Une alternative à Celery qui se veut plus simple, avec moins de configuration et des erreurs plus claires.

ARQ / SAQ

Des bibliothèques basées sur Redis et asyncio, parfaites si votre projet est déjà 100% asynchrone.

10

Tests & QA : Garantir la Fiabilité

De l'unitaire à l'intégration : coder avec confiance

Un code sans tests est un code "legacy" dès sa naissance. En Python Web, tester permet de s'assurer que vos routes renvoient les bons statuts, que vos modèles calculent correctement et que vos migrations n'ont rien cassé. Pytest est l'outil standard pour cette mission.

1. La Pyramide des Tests

Pour une stratégie efficace, privilégiez une large base de tests unitaires (rapides) et moins de tests de bout-en-bout (lents/fragiles).

graph TD UI["E2E / Selenium - Rare"] --> INT["Intégration / API - Moyen"] INT --> UNIT["Unitaire / Logique - Nombreux"] style UNIT fill:var(--primary),color:white

2. La Puissance de Pytest

Fini la lourdeur de unittest. Pytest utilise de simples assert et un système de Fixtures révolutionnaire.

conftest.py (Fixtures)
import pytest
from main import app
from fastapi.testclient import TestClient

@pytest.fixture
def client():
    # Setup : crée l'objet avant le test
    with TestClient(app) as c:
        yield c
    # Teardown : nettoyage auto après

3. Mocking : Isoler pour Mieux Tester

Si votre fonction appelle une API externe ou une DB lente, simulez son comportement pour que vos tests restent rapides et déterministes.

test_logic.py (Mock)
from unittest.mock import MagicMock

def test_payment_process(mocker):
    # On remplace l'appel réel à Stripe
    mock_stripe = mocker.patch("services.stripe.charge")
    mock_stripe.return_value = {"status": "success"}
    
    res = process_order(100)
    assert res is True
    mock_stripe.assert_called_once()

4. Mesurer la Qualité : Coverage

Le Code Coverage indique le pourcentage de lignes de code exécutées par vos tests. Visez 80%+, mais rappelez-vous : 100% de couverture ≠ 0 bugs.

📊 Rapports HTML

pytest --cov-report html génère un site web pour visualiser les lignes non testées.

🏗️ CI/CD

Bloquez le déploiement si la couverture descend en dessous d'un certain seuil (Security Gate).

🧪 TDD

Test-Driven Development : Écrire le test (rouge), coder (vert), refactoriser.

💡 Tip: DB de Test

N'utilisez jamais votre DB de prod ou de dev pour les tests. Configurez une base SQLite en mémoire ou un container Docker dédié qui est réinitialisé par les fixtures.

➕ Outils de Linting & Formatage

Ruff / Flake8

Analyse statique pour détecter les erreurs de syntaxe, les imports inutilisés ou le code mort.

Black / Blue

Formatage automatique du code pour que tout le projet respecte strictement la PEP 8 sans débat.

11

Serveurs App : WSGI, ASGI & Production

Passer du serveur de développement à une architecture robuste

Le serveur fourni par flask run ou manage.py runserver n'est JAMAIS destiné à la production. Il est lent, peu sécurisé et monothread. En production, on utilise un Serveur d'Application et un Reverse Proxy.

1. L'Architecture Standard

En production, on ne branche jamais l'application directement sur Internet. On utilise Nginx comme "bouclier" et Gunicorn/Uvicorn comme moteur de calcul.

graph LR U["🌍 Utilisateur"] -->|Port 80/443| N["🛡️ Nginx / Caddy"] N -->|Proxy Pass| S["⚙️ Gunicorn / Uvicorn"] S -->|WSGI / ASGI| A["🐍 App Python"]

2. Pourquoi Nginx ?

  • Fichiers Statiques : Nginx sert les images/CSS/JS bien plus vite que Python.
  • 🔒 SSL / TLS : C'est là qu'on gère les certificats (Certbot/LetsEncrypt).
  • 🧼 Buffering : Protège Python des clients lents qui "bloquent" les connexions.

3. Commandes de Production

Gunicorn (WSGI - Flask/Django)
# -w : Nombre de workers (2 * CPU + 1)
# -b : Bind sur une adresse
gunicorn -w 4 -b 0.0.0.0:8000 app:app
Uvicorn (ASGI - FastAPI)
uvicorn main:app \
  --host 0.0.0.0 \
  --port 8000 \
  --workers 4

4. Gestion des Workers

Type de Worker Framework Usage Idéal
Sync (Default) Django / Flask Requêtes rapides, calculs CPU standards.
Gevent / Eventlet Flask Requêtes avec beaucoup d'attente I/O sans async.
UvicornWorker FastAPI Tout ce qui utilise async/await.
💡 Tip: WhiteNoise (Django)

Pour les déploiements Cloud (Heroku, Docker) où Nginx n'est pas toujours disponible, WhiteNoise permet à votre application Python de servir elle-même ses fichiers statiques avec une efficacité proche de Nginx.

➕ Configuration Nginx simplifiée
/etc/nginx/sites-available/myapp
server {
    listen 80;
    server_name myapp.com;

    location /static/ {
        alias /path/to/static/;
    }

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
12

Docker : Conteneuriser pour l'Éternité

Isolation, reproductibilité et déploiement simplifié

Docker permet d'emballer votre application Python, ses dépendances et sa configuration dans une Image immuable. "Ça marche sur ma machine" devient "Ça marche partout où il y a Docker".

1. Anatomie d'un Dockerfile Pro

L'ordre des instructions est crucial pour profiter du cache des couches. Copiez toujours les fichiers de dépendances avant le code.

Dockerfile
# 1. Image de base légère
FROM python:3.11-slim

# 2. Variables d'environnement Python
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

WORKDIR /app

# 3. Cache des dépendances
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 4. Copie du code
COPY . .

CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:8000", "app:app"]

2. Cycle de Vie & Couches

graph TD DF["Dockerfile"] -->|docker build| IMG["Image Immuable"] IMG -->|docker run| C1["Conteneur A"] IMG -->|docker run| C2["Conteneur B"] C1 -->|docker stop| STOP["Arrêté"] C1 -->|docker rm| DEL["Supprimé"]

3. Multi-Stage Builds

Utilisez une image lourde pour compiler (ex: avec gcc) et une image ultra-légère pour l'exécution finale. Votre image passe de 800Mo à 150Mo.

Optimisation extrême
# STAGE 1: Builder
FROM python:3.11-slim as builder
RUN apt-get update && apt-get install -y gcc libpq-dev
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt

# STAGE 2: Final
FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /root/.local /root/.local
ENV PATH=/root/.local/bin:$PATH
COPY . .
CMD ["python", "main.py"]

4. Orchestration avec Docker Compose

Ne lancez pas vos conteneurs à la main. Définissez toute votre stack (App, DB, Redis) dans un seul fichier YAML.

docker-compose.yml
services:
  web:
    build: .
    ports: ["8000:8000"]
    depends_on: [db]
    environment: [DATABASE_URL=postgres://user:pass@db:5432/db]

  db:
    image: postgres:15-alpine
    volumes: ["postgres_data:/var/lib/postgresql/data"]

volumes:
  postgres_data:
💡 Volumes vs Bind Mounts

Utilisez les Volumes pour la persistance des données (DB) en prod. Utilisez les Bind Mounts pour le développement (partage de dossier code entre l'hôte et le conteneur).

➕ Commandes Essentielles Command Line
Commande Action
docker build -t app:v1 . Créer une image à partir du Dockerfile.
docker ps -a Lister tous les conteneurs (actifs ou non).
docker exec -it ID sh Entrer dans un conteneur en cours d'exécution.
docker compose up -d Lancer toute la stack en arrière-plan.
docker system prune ⚠️ Nettoyer tout ce qui est inutile (images orphelines, etc).
13

Toolkit : Références & Ressources

L'arsenal indispensable pour tout développeur Python Web

Maîtriser Python Web ne s'arrête pas au code. C'est aussi savoir choisir les bons outils de débogage, consulter les documentations officielles et utiliser les meilleurs outils de test d'API.

1. Librairies Incontournables

Domaine Librairie Pourquoi l'utiliser ?
HTTP Client httpx Successeur moderne de requests, gère le async nativement.
Validation pydantic La référence pour la validation de données par types.
Sécurité passlib[argon2] Le standard pour le hashing sécurisé des mots de passe.
Utils python-dotenv Indispensable pour charger ses fichiers .env.

2. L'Arsenal de l'API Tester

🚀 HTTPie

L'alternative moderne à curl en ligne de commande. Coloré et intuitif.

📡 Bruno / Insomnia

Des alternatives légères et souvent open-source à Postman pour tester vos endpoints REST/GraphQL.

🔍 DevTools

L'onglet Network de votre navigateur est votre meilleur ami pour inspecter les requêtes HTTP.

3. Mémo Commandes (Cheat Sheet)

Environnement
# Créer et activer un venv
python -m venv .venv
source .venv/bin/activate # Linux
.venv\Scripts\activate   # Windows

# Gérer les packages
pip install -r requirements.txt
pip freeze > requirements.txt
Docker Quickstart
# Lancer la stack complète
docker-compose up --build

# Voir les logs en direct
docker logs -f my_app_container

# Accéder à la DB
docker exec -it db_cont psql -U user

4. Liens Précis (Docs Officielles)

📚 Curated List: Awesome Python

Pour aller plus loin, consultez le dépôt GitHub Awesome Python qui liste les meilleures librairies pour chaque besoin imaginable.

Testez vos connaissances