Vue d'ensemble

Django 3.1, publié le 4 août 2020, poursuit la transition vers l'asynchrone initiée par Django 3.0. Les vues, les middlewares et les tests supportent désormais nativement async/await. L'autre nouveauté marquante est JSONField, disponible pour toutes les bases de données supportées, et non plus seulement PostgreSQL. Cette version ajoute également le support de pathlib.Path dans les réglages.

Fonctionnalités principales

JSONField pour toutes les bases de données

Le champ JSONField du module django.db.models fonctionne désormais avec SQLite, MySQL, MariaDB et PostgreSQL. Jusqu'ici, seul django.contrib.postgres.fields.JSONField était disponible. Le nouveau champ universel prend en charge les lookups comme __contains, __has_key et les chemins avec des transformations clé/index.

python
from django.db import models

class Produit(models.Model):
    nom = models.CharField(max_length=100)
    specifications = models.JSONField(default=dict)
    etiquettes = models.JSONField(default=list)

# Création avec des données JSON
ecran = Produit.objects.create(
    nom='Écran 27 pouces',
    specifications={
        'resolution': '2560x1440',
        'taille': 27,
        'connectique': ['HDMI', 'DisplayPort', 'USB-C'],
        'dalle': {'type': 'IPS', 'frequence': 144},
    },
    etiquettes=['informatique', 'bureau', 'promo'],
)

# Filtrage par clé imbriquée
ecrans_ips = Produit.objects.filter(
    specifications__dalle__type='IPS'
)

# Vérifier la présence d'une clé
avec_connectique = Produit.objects.filter(
    specifications__has_key='connectique'
)

# Filtrer par contenu de liste
promos = Produit.objects.filter(
    etiquettes__contains=['promo']
)

Vues et middlewares asynchrones

Django 3.1 permet de définir des vues avec async def. Le framework détecte automatiquement si la vue est synchrone ou asynchrone et adapte son exécution. Les middlewares peuvent également définir des méthodes asynchrones. Cela ouvre la voie aux appels HTTP non bloquants et aux connexions WebSocket directement dans les vues Django.

python
import httpx
from django.http import JsonResponse

# Vue asynchrone
async def meteo_ville(request, ville):
    """Récupère la météo via une API externe sans bloquer."""
    async with httpx.AsyncClient() as client:
        reponse = await client.get(
            f'https://api.meteo.example/v1/{ville}'
        )
    donnees = reponse.json()
    return JsonResponse({
        'ville': ville,
        'temperature': donnees['temp'],
        'description': donnees['description'],
    })

# Middleware asynchrone
class JournalisationAsyncMiddleware:
    async_capable = True

    def __init__(self, get_response):
        self.get_response = get_response

    async def __call__(self, request):
        import time
        debut = time.monotonic()
        response = await self.get_response(request)
        duree = time.monotonic() - debut
        print(f"{request.path} : {duree:.3f}s")
        return response

Support de pathlib.Path

Les réglages Django acceptent désormais les objets pathlib.Path partout où un chemin de fichier est attendu. Le fichier settings.py généré utilise Path au lieu de os.path pour BASE_DIR.

python
# settings.py (généré par Django 3.1+)
from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent.parent

# Plus besoin de os.path.join
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

STATIC_ROOT = BASE_DIR / 'staticfiles'
MEDIA_ROOT = BASE_DIR / 'media'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR / 'templates'],
    },
]

Sources