Vue d'ensemble

Django 4.2, publié le 3 avril 2023, est une version LTS (Long Term Support) qui recevra des mises à jour de sécurité jusqu'en avril 2026. Sa nouveauté la plus attendue est le support officiel de Psycopg 3, le nouveau pilote PostgreSQL pour Python, plus rapide et nativement asynchrone.

Cette version permet également d'ajouter des commentaires directement sur les colonnes et les tables de la base de données, et introduit le paramètre update_conflicts dans bulk_create() pour gérer les upserts de manière élégante.

Fonctionnalités principales

Support de Psycopg 3

Django supporte désormais officiellement Psycopg 3 (psycopg) comme pilote PostgreSQL, en plus de Psycopg 2 (psycopg2). Psycopg 3 offre un support natif de asyncio, une meilleure gestion des types, un pipeline de requêtes et des performances améliorées pour les requêtes paramétrées.

python
# settings.py — Utiliser Psycopg 3
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        # Psycopg 3 est utilisé automatiquement si installé
        # pip install psycopg[binary]
        'NAME': 'mon_projet',
        'USER': 'django_user',
        'PASSWORD': 'secret',
        'HOST': 'localhost',
        'PORT': '5432',
        'OPTIONS': {
            # Options spécifiques à Psycopg 3
            'pool': {
                'min_size': 2,
                'max_size': 10,
            },
        },
    },
}

# Pour forcer Psycopg 2 (rétro-compatibilité)
# pip install psycopg2-binary
# Django détecte automatiquement la version disponible

# Vérifier quel pilote est utilisé
from django.db import connection
print(connection.pg_version)  # ex: 150004
print(type(connection.connection))  # psycopg.Connection ou psycopg2

Commentaires sur les colonnes et les tables

Le nouvel attribut db_comment sur les champs et l'option Meta.db_table_comment permettent d'ajouter des commentaires SQL directement dans le schéma de la base de données. Ces commentaires sont visibles dans les outils d'administration de la base et facilitent la documentation du schéma pour les équipes DBA.

python
from django.db import models

class Client(models.Model):
    """Modèle client avec commentaires de base de données."""
    nom = models.CharField(
        max_length=100,
        db_comment='Nom complet du client (personne physique ou morale)',
    )
    siret = models.CharField(
        max_length=14,
        blank=True,
        db_comment='Numéro SIRET (14 chiffres) pour les entreprises',
    )
    score_credit = models.IntegerField(
        default=0,
        db_comment='Score interne de 0 à 1000, calculé mensuellement',
    )
    date_inscription = models.DateTimeField(
        auto_now_add=True,
        db_comment="Date d'inscription automatique (UTC)",
    )

    class Meta:
        db_table_comment = (
            'Clients actifs de la plateforme. '
            'Les clients archivés sont dans la table client_archive.'
        )

# En SQL, cela produit :
# COMMENT ON TABLE client IS 'Clients actifs de la plateforme. ...'
# COMMENT ON COLUMN client.nom IS 'Nom complet du client ...'

bulk_create avec update_conflicts

La méthode bulk_create() accepte désormais le paramètre update_conflicts pour réaliser des upserts (INSERT ... ON CONFLICT UPDATE). Cela permet d'insérer en masse tout en mettant à jour automatiquement les enregistrements existants, le tout en une seule requête SQL.

python
from django.db import models

class PrixProduit(models.Model):
    code_produit = models.CharField(max_length=20, unique=True)
    nom = models.CharField(max_length=200)
    prix = models.DecimalField(max_digits=10, decimal_places=2)
    stock = models.PositiveIntegerField(default=0)
    derniere_maj = models.DateTimeField(auto_now=True)

# Import quotidien depuis un fichier fournisseur
produits_fournisseur = [
    PrixProduit(code_produit='SKU-001', nom='Clavier mécanique', prix=89.99, stock=150),
    PrixProduit(code_produit='SKU-002', nom='Écran 27 pouces', prix=349.00, stock=42),
    PrixProduit(code_produit='SKU-003', nom='Souris ergonomique', prix=59.50, stock=200),
    # ... des milliers de produits
]

# Upsert en une seule requête : insère les nouveaux,
# met à jour prix et stock pour les existants
PrixProduit.objects.bulk_create(
    produits_fournisseur,
    update_conflicts=True,
    unique_fields=['code_produit'],
    update_fields=['prix', 'stock'],
)

# SQL généré :
# INSERT INTO prix_produit (code_produit, nom, prix, stock)
# VALUES (...), (...), (...)
# ON CONFLICT (code_produit)
# DO UPDATE SET prix = EXCLUDED.prix, stock = EXCLUDED.stock

Sources