Python Framework

Django

Django est le framework web Python « batteries-included ». Modèles, migrations, ORM, templates, authentification et interface d'administration de production sont fournis d'office : vous décrivez vos données et Django s'occupe du reste.

intermediate16 min readUpdated 16 sept. 2026
blog/views.py
python
# blog/views.py
from django.shortcuts import get_object_or_404, render

from .models import Post


def post_detail(request, slug):
    post = get_object_or_404(
        Post.objects.select_related("author"),
        slug=slug,
        status=Post.Status.PUBLISHED,
    )
    return render(request, "blog/post_detail.html", {"post": post})
Sortie
2005
Langage
Python
Pattern
MTV (Model–Template–View)
Base de données
ORM avec migrations
Admin
Intégré
Version actuelle
5.x

Pourquoi c'est important

Pourquoi Django inclut tout

Batteries included

L'ORM, les migrations, les templates, les formulaires, l'authentification, les sessions et un panneau d'administration sont fournis nativement, versionnés et testés ensemble.

Sécurisé par défaut

La protection CSRF, le hachage des mots de passe, les headers anti-clickjacking et les requêtes protégées contre les injections SQL sont activés, sauf si vous les désactivez délibérément.

Une vraie interface admin gratuite

Enregistrez un modèle et obtenez une interface CRUD avec recherche et filtres pour votre équipe, transformant la création d'outils internes en un travail de quelques heures.

Le tableau complet

Modèle, template, vue

Un modèle possède les données, un template les affiche, et une vue connecte les deux à une requête.

Modèle

Persister

Une classe Python correspond à une table. Les champs décrivent les colonnes, les relations décrivent les jointures, et les migrations font évoluer le schéma.

Template

Rendre

Le langage de template de Django mélange HTML avec un ensemble restreint de tags et de filtres, avec l'auto-échappement activé par défaut.

Vue

Gérer

Une vue reçoit une requête et renvoie une réponse. Elle interroge l'ORM, applique une logique et choisit un template ou un code de statut.

HTML5 en un coup d'oeil

La boîte à outils Django

Modèles et ORM

Définissez des classes, obtenez des tables. Interrogez vos données avec Python plutôt qu'avec du SQL construit sous forme de chaînes.

Dispatcher d'URL

Mappez des chemins vers des vues avec des patterns path() et des routes nommées.

Vues

Vues basées sur des fonctions pour la clarté, vues basées sur des classes et vues génériques pour la réutilisation.

Admin

Un back-office généré pour chaque modèle que vous enregistrez.

Formulaires

Des formulaires déclaratifs qui valident, affichent et protègent contre les falsifications.

Django REST Framework

Serializers, viewsets et routers transforment vos modèles en API JSON.

Un bref aperçu

D'un outil de rédaction à un pilier du web

  1. 2003

    Né dans une rédaction

    Des développeurs du Lawrence Journal-World créent un framework interne pour respecter des délais quotidiens.

    03
  2. 2005

    Sortie publique

    Django devient open-source et prend le nom du guitariste de jazz Django Reinhardt.

    05
  3. 2008

    La Django Software Foundation

    Une organisation à but non lucratif prend la direction, séparant le projet de toute entreprise unique.

    08
  4. 2014

    Migrations intégrées

    Les migrations de schéma deviennent une fonctionnalité native, remplaçant les outils tiers.

    14
  5. 2019

    L'arrivée de l'async

    Django 3.0 ajoute le support ASGI, ouvrant la voie aux vues asynchrones et aux channels.

    19
  6. Aujourd'hui

    Un standard mature

    Django 5.x associe un ORM stable au support async et à des indices de type (type hints) robustes.

    Aujourd'hui

Le guide complet

Django: Tout ce que vous devez savoir

Qu’est-ce que Django ?

Django est un framework web pour Python dit « batteries-included » (clé en main). Alors que la plupart des frameworks vous demandent de choisir votre couche de base de données, votre moteur de templates, un panneau d’administration et un système d’authentification, Django vous fournit tout cela dans un ensemble cohérent. Vous décrivez vos données et vos pages, et le framework s’occupe des parties fastidieuses et sensibles en termes de sécurité.

Il a été conçu en 2003 pour la rédaction d’un journal — une équipe qui devait déployer des fonctionnalités rapidement sans craquer sous la pression des délais. Cette origine est flagrante : Django privilégie les conventions, l’explicite et une philosophie du type « il doit y avoir une seule manière évidente de faire les choses ». C’est pourquoi il reste l’un des choix les plus fiables pour les sites riches en contenu et pilotés par les données.

Si vous avez déjà passé une semaine entière à tenter de faire fonctionner ensemble un ORM, un outil de migration et un système d’authentification, Django est le framework qui vous fait gagner cette semaine.

Le pattern MTV

Django appelle son architecture MTV : Model, Template, View. C’est le même concept que le MVC, mais avec des noms différents, ce qui peut être déroutant au début.

  • Un model est une classe Python qui décrit une table et ses lignes.
  • Un template est un fichier HTML avec des placeholders qui permet d’afficher les données.
  • Une view est une fonction ou une classe qui reçoit une requête, demande des données aux models et renvoie une réponse.

Le contrôleur, au sens classique du MVC, est Django lui-même. Le répartiteur d’URL (URL dispatcher) décide quelle view est exécutée, et le framework assure la médiation entre ces trois couches. Une fois ce schéma compris, tout le reste dans Django devient un simple détail plutôt qu’un mystère.

Modèles et ORM

Un modèle est une classe qui hérite de models.Model. Chaque attribut est un champ, et chaque champ devient une colonne.

# blog/models.py
from django.conf import settings
from django.db import models


class Post(models.Model):
    title = models.CharField(max_length=200)
    slug = models.SlugField(max_length=200, unique=True)
    body = models.TextField()
    author = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="posts",
    )
    published_at = models.DateTimeField(null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ["-created_at"]

    def __str__(self):
        return self.title

CharField nécessite un max_length car il correspond à un VARCHAR borné. TextField n’est pas borné. auto_now_add=True définit une valeur uniquement lors de la création, tandis que auto_now=True se met à jour à chaque sauvegarde. null contrôle la base de données et blank contrôle la validation du formulaire ; ils sont séparés intentionnellement.

Les relations sont explicites :

  • ForeignKey est une relation plusieurs-à-un (many-to-one).
  • ManyToManyField crée une table de jointure.
  • OneToOneField est une clé étrangère unique.

Le related_name définit l’accesseur inverse, permettant ainsi à user.posts.all() de fonctionner depuis l’autre côté. L’argument on_delete est requis car Django refuse de deviner ce qui doit arriver aux enfants lorsqu’un parent est supprimé.

Migrations

Les migrations sont des fichiers versionnés qui décrivent les modifications apportées à votre schéma. Vous écrivez le modèle, Django écrit le SQL.

python manage.py makemigrations blog
python manage.py migrate

makemigrations inspecte vos modèles et produit une migration ; migrate applique les migrations en attente à la base de données. Comme les fichiers de migration résident dans votre dépôt, chaque environnement finit par avoir le même schéma, et les changements peuvent être revus comme n’importe quel autre code. Ne modifiez jamais une migration déjà appliquée — ajoutez-en plutôt une nouvelle.

Querysets et relations

L’ORM retourne des querysets, qui sont “lazy” (chargement différé) : ils ne sont pas exécutés tant que vous ne les itérez pas, ne les découpez pas (slice) ou ne les évaluez pas. Cette approche vous permet de chaîner des filtres et de n’exécuter qu’une seule requête finale.

Post.objects.filter(status="PB").exclude(author=user).order_by("-published_at")[:10]

Parmi les méthodes de queryset utiles, on trouve filter, exclude, get, first, exists, count, values, values_list, annotate et aggregate. Les expressions F() vous permettent de référencer une colonne de la base de données, et les objets Q() vous permettent de construire des conditions OR.

from django.db.models import Count, Q

published = Post.objects.filter(
    Q(status="PB") | Q(published_at__isnull=False)
).annotate(comment_count=Count("comments"))

C’est là que l’ORM prouve toute son utilité : les requêtes complexes restent en Python, sont paramétrées de manière sécurisée et sont composables.

Éviter le problème du N+1

L’erreur de performance la plus courante dans Django est la requête N+1. Cela se produit lorsque vous accédez à un objet lié à l’intérieur d’une boucle, déclenchant ainsi une requête supplémentaire par ligne.

# 1 query for posts, then 1 query per post for the author
for post in Post.objects.all():
    print(post.author.username)

select_related fusionne les relations à valeur unique dans la requête d’origine, tandis que prefetch_related exécute une seconde requête et assemble les résultats en Python pour les relations à valeurs multiples.

# 1 query for posts joined with author, 1 for the comments
posts = Post.objects.select_related("author").prefetch_related("comments")

Utilisez select_related pour les clés étrangères (foreign keys) et les champs one-to-one, et prefetch_related pour les relations many-to-many et les relations inverses. Si une page semble lente, comptez les requêtes avec la Django Debug Toolbar avant d’optimiser quoi que ce soit d’autre.

Vues : fonctions et classes

Une vue est tout élément appelable qui prend une requête en entrée et retourne une réponse. Les vues basées sur des fonctions sont le point de départ le plus simple.

# blog/views.py
from django.http import JsonResponse
from django.shortcuts import get_object_or_404, render

from .models import Post


def post_list(request):
    posts = Post.objects.filter(status="PB").select_related("author")
    return render(request, "blog/post_list.html", {"posts": posts})


def post_api(request, slug):
    post = get_object_or_404(Post, slug=slug)
    return JsonResponse({"title": post.title, "body": post.body})

Les vues basées sur des classes sacrifient un peu de directivité au profit de la réutilisation. ListView, DetailView, CreateView, UpdateView et DeleteView implémentent les modèles CRUD standards, et les mixins vous permettent de composer des comportements.

from django.views.generic import ListView

from .models import Post


class PostListView(ListView):
    model = Post
    template_name = "blog/post_list.html"
    context_object_name = "posts"
    paginate_by = 10

    def get_queryset(self):
        return Post.objects.filter(status="PB").select_related("author")

Utilisez des vues basées sur des fonctions pour la logique ponctuelle et des vues basées sur des classes génériques pour les pages de liste et de détail standards. Mélanger les deux au sein d’une base de code est tout à fait normal et sain.

URLs

Le dispatcher d’URL mappe un chemin vers une vue. Les patterns se trouvent dans urls.py, et chaque application peut conserver les siens.

# config/urls.py
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path("admin/", admin.site.urls),
    path("blog/", include("blog.urls")),
]
# blog/urls.py
from django.urls import path

from . import views

app_name = "blog"

urlpatterns = [
    path("", views.post_list, name="post_list"),
    path("<slug:slug>/", views.post_detail, name="post_detail"),
]

Les convertisseurs tels que <int:id> et <slug:slug> valident et typent le segment capturé. Nommez toujours vos routes et effectuez l’ingénierie inverse avec reverse() ou le tag {% url %} au lieu de coder les chemins en dur, afin que les liens survivent à un renommage.

Templates

Les templates sont du HTML enrichi d’un langage volontairement limité. L’auto-échappement (autoescaping) est activé par défaut, ce qui vous protège des failles XSS.

{% extends "base.html" %}
{% block content %}
  <h1>{{ post.title }}</h1>
  <p>By {{ post.author.username }} on {{ post.published_at|date:"F j, Y" }}</p>
  <div>{{ post.body|linebreaks }}</div>
{% endblock %}

{% extends %} permet l’héritage de templates, {% include %} permet de partager des fragments, et des filtres tels que date, linebreaks et default servent à formater les valeurs. Il n’est pas possible d’exécuter du code Python arbitraire dans les templates ; si un template nécessite de la logique, celle-ci doit se trouver dans la vue ou le modèle.

Formulaires et validation

Le système de formulaires de Django valide les entrées, génère le HTML et protège contre les falsifications grâce à un jeton CSRF.

# blog/forms.py
from django import forms

from .models import Post


class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ["title", "slug", "body", "status", "published_at"]
        widgets = {"body": forms.Textarea(attrs={"rows": 12})}
# blog/views.py
from django.shortcuts import redirect, render

from .forms import PostForm


def post_create(request):
    if request.method == "POST":
        form = PostForm(request.POST)
        if form.is_valid():
            post = form.save(commit=False)
            post.author = request.user
            post.save()
            return redirect(post)
    else:
        form = PostForm()

    return render(request, "blog/post_form.html", {"form": form})

Un ModelForm dérive ses champs et sa validation du modèle, ce qui évite tout décalage entre les deux. Des méthodes de nettoyage comme clean_slug permettent d’ajouter des règles personnalisées, et form.is_valid() collecte toutes les erreurs avant même d’interagir avec la base de données.

L’interface d’administration

L’admin est la fonctionnalité la plus sous-estimée de Django. En enregistrant un modèle, vous obtenez une interface pour le personnel qui permet la recherche, le filtrage et la gestion des permissions.

# blog/admin.py
from django.contrib import admin

from .models import Post


@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ["title", "author", "status", "published_at"]
    list_filter = ["status", "created_at"]
    search_fields = ["title", "body"]
    prepopulated_fields = {"slug": ["title"]}
    raw_id_fields = ["author"]
    date_hierarchy = "published_at"

C’est un outil interne complet en une douzaine de lignes. Pour les sites de contenu, l’admin est souvent le produit lui-même. C’est également une frontière de sécurité : les permissions du personnel, has_add_permission et has_change_permission contrôlent précisément ce que chaque rôle peut faire.

Paramètres et applications

Un projet Django est une collection d’apps, chacune étant un package autonome avec ses propres modèles, vues, templates et migrations. INSTALLED_APPS les liste, et settings.py contient la configuration.

INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "blog",
]

Évitez de stocker vos paramètres sensibles dans le contrôle de version. Lisez-les via des variables d’environnement, séparez les paramètres par environnement si le projet s’agrandit, et utilisez django-environ ou pydantic-settings pour les analyser. Les commandes django-admin startproject et startapp génèrent l’échafaudage de ces deux couches, vous n’avez donc jamais à vous soucier de la structure.

Paramètres de sécurité par défaut

Les réglages par défaut de Django sont conçus pour que la solution la plus sûre soit aussi la plus simple :

  • Les jetons CSRF protègent les formulaires modifiant l’état ; le middleware rejette les requêtes qui n’en possèdent pas.
  • L’injection SQL est évitée car les querysets paramètrent les valeurs.
  • Les failles XSS sont atténuées car les templates échappent les variables, sauf si vous choisissez de ne pas le faire avec |safe.
  • Les mots de passe sont hachés avec un algorithme moderne et des hashers configurables.
  • Le Clickjacking est bloqué par le middleware X-Frame-Options.
  • Le HTTPS est imposé en production via SECURE_SSL_REDIRECT, SESSION_COOKIE_SECURE et CSRF_COOKIE_SECURE.

Exécutez python manage.py check --deploy avant le déploiement. Cela signalera les paramètres que vous avez oubliés, ce qui est bien moins coûteux qu’un audit de sécurité.

Créer des API avec Django REST Framework

Pour les API JSON, Django REST Framework (DRF) est le compagnon standard. Un serializer décrit comment un modèle est converti en JSON et inversement, tandis qu’un viewset associé à un router génère les endpoints CRUD.

# blog/serializers.py
from rest_framework import serializers

from .models import Post


class PostSerializer(serializers.ModelSerializer):
    author = serializers.StringRelatedField()

    class Meta:
        model = Post
        fields = ["id", "title", "slug", "author", "status", "published_at"]
# blog/api.py
from rest_framework import viewsets

from .models import Post
from .serializers import PostSerializer


class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.select_related("author")
    serializer_class = PostSerializer
    lookup_field = "slug"
# blog/urls.py
from rest_framework.routers import DefaultRouter

from .api import PostViewSet

router = DefaultRouter()
router.register("posts", PostViewSet, basename="post")

urlpatterns = router.urls

Les serializers valident les entrées de la même manière que les formulaires, les viewsets fournissent nativement les actions de liste, création, récupération, mise à jour et suppression, et DRF peut générer des schémas OpenAPI pour votre API. L’ORM de Django et les conventions de DRF constituent une voie éprouvée pour les API en production.

Tests

Le test runner de Django crée une base de données de test vierge et fournit un client capable de simuler l’intégralité du pipeline de requête.

# blog/tests.py
from django.contrib.auth import get_user_model
from django.test import TestCase
from django.urls import reverse

from .models import Post


class PostDetailTests(TestCase):
    def setUp(self):
        user = get_user_model().objects.create_user("ada", password="secret")
        self.post = Post.objects.create(
            title="Hello",
            slug="hello",
            body="First post",
            author=user,
            status=Post.Status.PUBLISHED,
        )

    def test_detail_page_renders(self):
        response = self.client.get(reverse("blog:post_detail", args=["hello"]))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "Hello")

    def test_missing_post_returns_404(self):
        response = self.client.get(reverse("blog:post_detail", args=["nope"]))
        self.assertEqual(response.status_code, 404)

TestCase enveloppe chaque test dans une transaction et effectue un rollback, garantissant ainsi que les tests restent isolés et rapides. Utilisez django.test.Client pour les vues, l’ORM directement pour les modèles, et pytest-django si vous préférez les fixtures de pytest. Concentrez-vous sur le test du comportement et des permissions, plutôt que sur les détails d’implémentation.

Bonnes pratiques

  • Gardez vos modèles concentrés sur les données et les petites méthodes de domaine ; placez l’orchestration dans les services ou les vues.
  • Exécutez toujours select_related et prefetch_related dès qu’un template manipule des relations.
  • Utilisez ModelForm et les serializers de DRF afin que la validation soit centralisée au même endroit.
  • Nommez chaque URL et utilisez le mécanisme de résolution inverse (reverse) ; ne codez jamais les chemins en dur dans les templates ou le code.
  • Ajoutez des index de base de données pour les colonnes sur lesquelles vous effectuez des filtres ou des tris.
  • Conservez vos secrets dans des variables d’environnement et exécutez check --deploy avant la mise en production.
  • Considérez les migrations comme des artefacts de revue de code et ne modifiez jamais une migration déjà appliquée.
  • Testez le pipeline de requête avec Client et testez la logique métier directement.

Erreurs courantes

  • Itérer sur un queryset et modifier des relations à l’intérieur de la boucle, provoquant des requêtes N+1.
  • Utiliser null=True sur un champ texte au lieu d’une chaîne vide, puis se battre avec les vérifications None.
  • Placer la logique métier dans les templates parce que la vue semblait trop chargée.
  • Appeler save() dans une boucle au lieu de bulk_create ou bulk_update.
  • Oublier on_delete ou choisir CASCADE sans réfléchir à la perte de données.
  • Laisser DEBUG = True et un SECRET_KEY codé en dur en production.
  • Traiter l’admin comme un frontend destiné aux utilisateurs plutôt que comme un outil pour le staff.
  • Effectuer des requêtes avec len(queryset) au lieu de .count(), chargeant ainsi chaque ligne en mémoire.

Et après ?

Django vous offre un parcours complet et conventionnel pour créer une application web Python prête pour la production. Si vous préférez un cœur plus léger et plus de liberté dans vos choix, consultez ensuite le guide Flask. Si vous construisez une API JSON asynchrone moderne et que vous souhaitez utiliser les types pour la validation, tournez-vous vers FastAPI. Quel que soit votre choix, investissez dans votre base de données : l’indexation, les transactions et la planification des requêtes sont plus cruciales que le framework lui-même, et la roadmap backend approfondit ces notions. Le guide sur les REST APIs détaille les règles de conception qui rendent une API agréable à consommer.

En pratique

Quatre fichiers qui composent une app Django

Un modèle, une vue, un mappage d'URL et un enregistrement admin constituent la boucle principale d'un projet Django.

blog/models.py
from django.conf import settings
from django.db import models
from django.urls import reverse


class Post(models.Model):
    class Status(models.TextChoices):
        DRAFT = "DF", "Draft"
        PUBLISHED = "PB", "Published"

    title = models.CharField(max_length=200)
    slug = models.SlugField(max_length=200, unique=True)
    body = models.TextField()
    author = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="posts",
    )
    status = models.CharField(
        max_length=2,
        choices=Status.choices,
        default=Status.DRAFT,
    )
    published_at = models.DateTimeField(null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ["-created_at"]

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse("blog:post_detail", kwargs={"slug": self.slug})

Chargement d'objets liés

Accéder à une clé étrangère dans une boucle génère une requête par ligne. Récupérez la relation en amont et la page entière ne coûtera qu'une seule requête.

Préférer
posts = Post.objects.select_related("author")

for post in posts:
    print(post.author.username)
Éviter
posts = Post.objects.all()

for post in posts:
    # one extra query per iteration (the N+1 problem)
    print(post.author.username)

Gestion d'une ligne manquante

get_object_or_404 transforme une exception DoesNotExist en une réponse 404 appropriée et évite d'encombrer la vue avec des blocs try/except.

Préférer
from django.shortcuts import get_object_or_404

post = get_object_or_404(Post, slug=slug)
Éviter
from django.http import Http404

try:
    post = Post.objects.get(slug=slug)
except Post.DoesNotExist:
    raise Http404

Compromis

Django est-il le bon choix par défaut ?

Django privilégie l'exhaustivité et les conventions. C'est un atout quand on a une deadline, mais une contrainte quand on recherche quelque chose de léger.

Strengths

  • Tout est déjà là

    L'authentification, les sessions, les formulaires, l'ORM, les migrations et l'admin sont maintenus par un seul projet, ils s'imbriquent donc parfaitement sans code d'adaptation.

  • La sécurité par défaut

    Le framework se défend contre les bases de l'OWASP nativement, et la méthode documentée pour faire quelque chose est généralement la plus sûre.

  • Une communauté immense et stable

    Vingt ans de réponses, de packages et d'offres d'emploi signifient que presque chaque problème a déjà une solution éprouvée.

Trade-offs

  • Une courbe d'apprentissage réelle

    Les réglages, les apps, l'ORM et le langage de template représentent beaucoup d'informations à absorber. Le framework récompense plus la patience que l'expérimentation rapide.

  • Monolithique par conception

    Django suppose l'utilisation d'une base de données relationnelle et d'une architecture render-server ou API. Les microservices et les stacks exotiques vont à l'encontre de sa philosophie.

  • Le support async est jeune

    L'ORM est encore largement synchrone, donc mélanger des vues async avec du travail de base de données demande de la prudence et l'usage de threads.

FAQ

Foire aux questions

Keep learning

Related topics from the roadmap.

$ commencer à apprendre

Prêt à apprendre Django ?

Notre tutoriel interactif vous guide à travers Django pas à pas — avec des quiz et du vrai code que vous pouvez exécuter dans le navigateur.