L'écosystème Python propose une multitude de frameworks pour le développement web. Parmi les plus connus d'entre eux : Django et Flask.
Ces deux frameworks adoptent des philosophies différentes.
Django est connu pour son côté « batteries incluses », c'est-à-dire une solution complète et structurée, tandis que Flask est réputé pour sa légèreté et sa flexibilité.
Le choix d'un framework peut parfois être délicat, car il impacte l'évolutivité et la maintenance d'un projet.
Pour quelle raison pourriez-vous préférer l'un à l'autre ? C'est ce que nous allons voir dans cet article :)
Un peu d'histoire
Django
Le développement de Django a commencé en 2003 pour le journal local d'une ville aux Etats-Unis. Il a par la suite été publié comme un framework open-source en juillet 2005. C'est un framework solide avec près de 20 ans d'histoire.
Sa devise : « The web framework for perfectionists with deadlines » (Le framework web pour les perfectionnistes avec des "deadlines").
Django adopte une approche batteries incluses (tout compris). Il fournit dès l'installation tout un lot de fonctionnalités :
-
Un ORM puissant pour interagir avec la base de données
-
Une interface d'administration prête à l'emploi
-
Un système d’authentification et de gestion des utilisateurs complet
-
Un système de gestion des formulaires
-
Un moteur de template (Django Template Language)
-
Des fonctionnalités de sécurité
Comme vous pouvez le voir, de base Django propose une multitude de fonctionnalités prêtes à l'emploi, ce qui permet de se concentrer rapidement sur la logique de votre application.
Flask
Et si je vous disais que Flask est né d'un poisson d'avril ?
Flask a été publié en avril 2010 par Armin Ronacher, qui avait l'idée de créer un framework minimaliste. Finalement le framework a gagné en popularité et est largement utilisé dans l'écosystème Python.
Contrairement à Django, Flask adopte une approche de microframework :
-
Un noyeau basé sur Werkzeug (un framework WSGI)
-
Un moteur de template (Jinja 2)
-
Des fonctionnalités de sécurité
Et pour l'administration et l'ORM ?
Avec son approche minimaliste, Flask permet aux développeurs d'utiliser uniquement ce dont ils ont besoin. Il est possible d'étendre ses applications via des extensions comme Flask-SQLAlchemy en tant qu'ORM, Flask-Admin pour l'administration, ou encore Flask-Login pour la gestion de l'authentification mais vous devrez choisir et installer ces dépendances en fonction de vos projets.
Structure
Django : le framework MVT
Django suit le modèle MVT (Model-View-Template) :
-
Model représente les données et la logique métier
-
View gère la présentation et le traitement des requêtes
-
Template s'occupe du rendu HTML
Ce modèle MVT impose une structure de base avec comme exemple des fichiers avec chacun leurs responsabilités : models.py, views.py, urls.py. De plus, le framework incite à diviser le projet en applications.
Voici un exemple d'architecture Django :
mon_projet/
├── mon_projet/ # Répertoire de configuration du projet
│ ├── __init__.py
│ ├── settings.py # Paramètres du projet
│ ├── urls.py # Routes principales
│ ├── asgi.py
│ └── wsgi.py
│
├── accounts/ # Application "accounts"
│ ├── migrations/ # Migrations de base de données
│ ├── templates/ # Templates HTML spécifiques
│ ├── tests/ # Tests de l'application
│ ├── admin.py # Configuration de l'admin
│ ├── apps.py # Configuration de l'application
│ ├── forms.py # Formulaires
│ ├── models.py # Modèles de données
│ ├── urls.py # Routes de l'application
│ └── views.py # Vues et logique
│
├── store/ # Application "store"
│ ├── migrations/
│ ├── templates/
│ ├── tests/
│ ├── admin.py
│ ├── apps.py
│ ├── forms.py
│ ├── models.py
│ ├── urls.py
│ └── views.py
│
├── media/ # Fichiers téléversés
├── templates/ # Templates partagés
└── manage.py # Utilitaire de commande
Chaque fichier a un rôle bien défini, ce qui rend naturellement le code plus facile à maintenir. Avec cette architecture prédéfinie, vous pouvez vous concentrer sur l'ajout de modules personnalisés comme validators.py (pour des validateurs personnalisés) ou utils.py (pour des fonctions utilitaires).
Flask : liberté architecturale
Flask n'impose pas d'architecture prédéfinie, comme nous l'avons évoqué plus tôt dans l'article, Flask est un microframework. Ce qui vous donne davantage de liberté pour organiser votre code par rapport à Django (attention, je ne dis pas que c'est mieux !)
Par exemple, j'ai créé un projet (très très minimaliste) avec un unique main.py et ce code :
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hi_patrick():
return "<p>Salut Patrick !</p>"
Cette simplicité est une caractéristique majeure de Flask. Mais à mesure que votre projet grossit, vous adopterez certainement une structure organisée. Il est tout à fait possible d'adopter un pattern MVC (Model-View-Controller), ou tout autre structure.
Vous disposez d'une grande flexibilité, mais vous aurez des décisions à prendre sur l'architecture et l'organisation de votre code.
Voici un exemple d'architecture simple que l'on peut adopter pour un projet Flask :
src/
├── controllers/
├── models/
├── routes/
├── services/
├── __init__.py
├── app.py
└── config.py
Fonctionnalités principales de Django
Comme nous l'avons vu précédemment, Django est livré avec un ensemble complet de fonctionnalités prêtes à l'emploi.
ORM
Django intègre un mapping objet-relationnel, c'est-à-dire une interface qui permet de dialoguer avec une base de données en Python, sans écrire de SQL. Plusieurs bases de données sont officiellement supportées :
-
PostgreSQL
-
MariaDB
-
MySQL
-
Oracle
-
SQLite
L'un des grands avantages de l'ORM de Django est qu'il est possible de ne pas utiliser de SQL. Tout se fait en Python, de la définition des modèles jusqu'aux requêtes les plus complexes.
Par exemple, voici comment définir un modèle :
from django.db import models
class Article(models.Model):
titre = models.CharField(max_length=200)
contenu = models.TextField()
date_publication = models.DateTimeField(auto_now_add=True)
auteur = models.ForeignKey('Auteur', on_delete=models.CASCADE)
def __str__(self):
return self.titre
class Auteur(models.Model):
nom = models.CharField(max_length=100)
email = models.EmailField(unique=True)
def __str__(self):
return self.nom
Et des exemples de requêtes :
# Récupérer tous les articles
articles = Article.objects.all()
# Récupérer tous les articles d'un auteur spécifique
articles_jean = Article.objects.filter(auteur__nom='Jean Dupont')
# Récupérer les 5 articles les plus récents
articles_recents = Article.objects.order_by('-date_publication')[:5]
# Requête plus complexe : articles contenant le mot "Python" dans le titre,
# publiés ce mois-ci
from django.utils import timezone
import datetime
debut_mois = timezone.now().replace(day=1, hour=0, minute=0, second=0)
articles_python = Article.objects.filter(
titre__icontains='python',
date_publication__gte=debut_mois
)
Pour cette dernère requête, l'équivalent SQL serait :
SELECT * FROM blog_article
WHERE titre ILIKE '%python%'
AND date_publication >= '2025-04-01 00:00:00';
Avouez que c'est quand même plus simple avec Django, non ?
Moteur de template
Django possède son propre moteur de template qui permet de séparer la logique Python, et le code HTML. Il est possible d'exécuter des instructions conditionnelles, de faire des boucles, réutiliser des templates pour les étendre, et transformer les données avec des filtres.
<!-- product_list.html -->
{% extends "base.html" %}
{% block content %}
<h2>Nos produits</h2>
{% if products %}
<ul>
{% for product in products %}
<li>
<h3>{{ product.name|title }}</h3>
<p>Prix : {{ product.price|floatformat:2 }} €</p>
</li>
{% endfor %}
</ul>
{% else %}
<p>Aucun produit disponible.</p>
{% endif %}
{% endblock %}
Dans cet exemple :
-
L'héritage avec
{% extends %}qui permet de réutiliser le template de base, -
Les blocs avec
{% block %}pour définir des zones remplaçables, -
Les structures conditionnelles avec
{% if %}et{% else %}, -
Les boucles avec
{% for %}pour itérer, -
L'affichage de variables avec
{{ variable }}, -
Les filtres comme
|title(met en majuscule la première lettre) et|floatformat(formate les nombres décimaux).
Administration
Un projet Django comprend aussi une administration accessible pour gérer le contenu de la base de données. Même s'il est possible de la customiser, l'interface d'administration est prête à l'emploi.
Administration de django
Il existe plusieurs librairies pour personnaliser l'administration.
Par exemple, unfold-admin permet de moderniser l'interface :
Administration django unfold
Session et Authentification
Gestion des utilisateurs, des permissions, des groupes et des sessions, tout est disponible d'office avec Django. C'est-à-dire que Django conserve les données côté serveur entre plusieurs requêtes d'un même utilisateur. Il est possible de facilement récupérer toutes les informations sur un utilisateur connecté.
De plus, Django possède un modèle Utilisateur built-in, des fonctions de login, logout, la possibilité d'accéder aux informations d'un utilisateur directement via les requêtes, etc.
Voici quelques exemples de fonctions et classes :
-
Une classe qui permet le login
from django.contrib.auth.views import LoginView from django.urls import reverse_lazy class UserLoginView(LoginView): template_name = 'account/login.html' next_page = reverse_lazy('index') -
Il est possible de le faire avec une fonction
from django.contrib.auth import authenticate, login def my_view(request): username = request.POST["username"] password = request.POST["password"] user = authenticate(request, username=username, password=password) if user is not None: login(request, user) # Redirect to a success page. ... else: # Return an 'invalid login' error message. ... -
Un exemple de logout
from django.contrib.auth import logout def logout_view(request): logout(request) return redirect("index") -
Un exemple complet de reinitialisation de mot de passe
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.views import PasswordChangeView, PasswordResetView, PasswordResetConfirmView from django.shortcuts import render from django.urls import reverse_lazy class ChangePassword(LoginRequiredMixin, PasswordChangeView): template_name = "accounts/change-password.html" success_url = reverse_lazy("account:password-change-done") def password_change_done(request): return render(request, "accounts/change-password-done.html") # Bloc demande de mdp par mail class HeroResetPassword(PasswordResetView): template_name = "reset/password-reset.html" success_url = reverse_lazy("account:password-reset-done") email_template_name = "reset/password-email.html" def hero_reset_done(request): return render(request, "reset/password-reset-done.html") class HeroResetConfirm(PasswordResetConfirmView): template_name = "reset/password-reset-confirm.html" success_url = reverse_lazy("account:password-reset-complete") def hero_password_reset_complete(request): return render(request, "reset/password-reset-complete.html")
Formulaires
Django inclut un système de formulaires pour créer et gérer des formulaires. Les formulaires sont définis comme des classes Python, et sont composés de validateurs prédéfinis pour les champs. Une protection contre les attaques CSRF est incluse. Aussi, le rendu HTML est facilement modifiable.
Voici un exemple pour un formulaire de contact.
On commence par définir le formulaire en Python (sans forcément le relier à un modèle) :
# forms.py
from django import forms
class ContactForm(forms.Form):
nom = forms.CharField(max_length=100)
email = forms.EmailField(required=True)
message = forms.CharField(widget=forms.Textarea)
On gère ensuite l'affichage et la sauvegarde du formulaire dans une vue :
# views.py
from django.shortcuts import render, redirect
from .forms import ContactForm
def contact_view(request):
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
# Traitement des données
nom = form.cleaned_data['nom']
email = form.cleaned_data['email']
message = form.cleaned_data['message']
# Ici on pourrait sauvegarder dans la base de données
return redirect('success')
else:
form = ContactForm()
return render(request, 'contact.html', {'form': form})
Et on affiche le formulaire en quelques lignes avec le langage de template de Django :
<!-- contact.html -->
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Envoyer</button>
</form>
Dans cet exemple, le formulaire est défini dans forms.py, rendu dans le html et traité dans views.py.
Le rendu HTML par défaut est assez basique. Mais il existe des librairies comme django-crispy-forms, qui permet de créer des formulaires élégants.
Cette librairie est également compatible avec plusieurs frameworks CSS comme bootstrap ou tailwind.
Sécurité
Django est livré avec tout un lot de protections : XSS (Cross site scripting), CSRF (Cross site request forgery), injections SQL, clickjacking, et sécurité SSL/HTTPS.
La protection CSRF sert à empêcher qu'un site malveillant ne puisse à votre insu, vous faire faire des actions indésirables sur un site où vous êtes déjà connecté.
Par exemple, vous êtes connectés à votre banque en ligne. Dans un autre onglet, vous visitez un site douteux. Sans protection CSRF, ce site pourrait essayer d'envoyer de l'argent depuis votre compte bancaire en profitant du fait que vous êtes déjà connecté à la banque. La protection CSRF bloque ce genre d'attaque.
La protection XSS empêche l'injection de code JavaScript dans les pages web. Django protège contre ce risque en transformant automatiquement les caractères dangereux comme (<, >, " et ') dans les variables affichées dans les templates. Si un utilisateur essaye d'injecter du JavaScript, ce code sera affiché comme du texte et ne sera pas exécuté par le navigateur.
Fonctionnalités principales de Flask
Un noyau minimaliste...
Flask est livré avec :
-
un serveur de développement
-
un moteur de template (Jinja2) très ressemblant à celui de Django
-
un système de sessions sécurisé via cookies (mais pas d'authentification).
Flask n'est donc pas livré avec un ORM, une administration ou encore un système de formulaires. Mais nous allons voir que Flask est facilement extensible.
...et extensible
Voici quelques extensions populaires qui permettent facilement d'ajouter des fonctionnalités :
-
Flask-SQLAlchemy, un ORM qui supporte différentes bases de données
-
Flask-Admin, pour créer une interface d'administration
-
Flask-Login, pour la gestion de l'authentification
-
Flask-WTF, pour la création et la validation des formulaires avec protection CSRF
-
Flask-Babel, pour l'internationalisation
Vous avez juste à ajouter les extensions dont vous avez besoin.
Flask ou Django ?
Il fallait bien poser cette question à un moment donné !
Les deux ne sont pas en compétition, mais leur approche est différente.
Si vous avez besoin d'une application web complète avec de nombreuses fonctionnalités, alors la solution clés en main Django est pour vous. Si vous avez besoin d'une application légère, ou que vous voulez choisir vos propres composants, Flask sera plus adapté.
Flask est idéal pour :
-
Les applications simples,
-
Les APIs REST légères,
-
Les projets à déployer sur des environnements contraints (ex : Raspberry Pi),
-
Les microservices,
-
Les situations où vous souhaitez choisir chaque composant.
Flask est utilisé par des entreprises telles que Reddit, Netflix et LinkedIn.
Django est recommandé pour :
-
Les applications web complexes,
-
Les projets à forte croissance,
-
Les applications nécessitant une interface d'administration et une gestion avancée des bases de données.
Django est utilisé par des entreprises comme Meta (Instagram), Udemy et la NASA.
Tout dépend de la nature de votre projet, de vos contraintes, et de vos préférences en tant que développeur.
Et si vous souhaitez apprendre Django, vous pouvez commencer avec notre parcours complet dédié à ce framework !