Notes de publication de Django 5.1

7 août 2024

Bienvenue dans Django 5.1 !

Ces notes de publications couvrent les nouvelles fonctionnalités, ainsi que certaines modifications non rétrocompatibles dont il faut être au courant lors de la mise à jour depuis Django 5.0 ou des versions plus anciennes. Nous avons commencé le processus d’obsolescence de certaines fonctionnalités.

Voir le guide Mise à jour de Django à une version plus récente si vous mettez à jour un projet existant.

Compatibilité Python

Django 5.1 supports Python 3.10, 3.11, 3.12, and 3.13 (as of 5.1.3). We highly recommend and only officially support the latest release of each series.

Quoi de neuf dans Django 5.1

Balise de gabarit {% querystring %}

Django 5.1 introduit la balise de gabarit {% querystring %} pour simplifier la modification des paramètres de requête dans les URL, ce qui rend plus facile la génération de liens en conservant les paramètres de requête existants tout en en ajoutant ou en en modifiant certains.

Par exemple, la navigation dans la pagination et les paramètres de requête dans les gabarits peut se révéler compliquée. Imaginez ce fragment de gabarit qui génère dynamiquement une URL pour naviguer vers la page suivante dans le cadre d’une vue paginée :

{# Linebreaks added for readability, this should be one, long line. #}
<a href="?{% for key, values in request.GET.iterlists %}
  {% if key != "page" %}
    {% for value in values %}
      {{ key }}={{ value }}&amp;
    {% endfor %}
  {% endif %}
{% endfor %}page={{ page.next_page_number }}">Next page</a>

En utilisant la nouvelle balise de gabarit, le contenu ci-dessus devient magiquement :

<a href="{% querystring page=page.next_page_number %}">Next page</a>

Réserves de connexions PostgreSQL

Django 5.1 introduit aussi la prise en charge des réserves de connexion (pool) pour PostgreSQL. Comme le temps d’établissement d’une connexion peut être relativement lent, conserver des connexions ouvertes peut réduire la latence.

Pour utiliser une réserve de connexions avec psycopg, vous pouvez définir l’option "pool" dans OPTIONS, soit sous forme de dictionnaire à passer à ConnectionPool, soit avec la valeur True pour utiliser les valeurs par défaut de ConnectionPool:

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        # ...
        "OPTIONS": {
            "pool": {
                "min_size": 2,
                "max_size": 4,
                "timeout": 10,
            }
        },
    },
}

Intergiciel pour exiger l’authentification par défaut

Le nouvel intergiciel LoginRequiredMiddleware redirige toutes les requêtes non authentifiées ver une page de connexion. Les vues peuvent autoriser des requêtes non authentifiées en utilisant le nouveau décorateur login_not_required().

LoginRequiredMiddleware respecte les valeurs login_url et redirect_field_name définies via le décorateur login_required(), mais ne prend pas en charge la définition de login_url ou redirect_field_name via la classe LoginRequiredMixin.

Pour activer cette nouvelle fonctionnalité, ajoutez "django.contrib.auth.middleware.LoginRequiredMiddleware" à votre réglage MIDDLEWARE.

Fonctionnalités mineures

django.contrib.admin

django.contrib.auth

  • Le nombre d’itération par défaut du hacheur de mot de passe PBKDF2 a été augmenté de 720’000 à 870’000.

  • Le paramètre parallelism par défaut de ScryptPasswordHasher a été augmenté de 1 à 5, pour suivre les recommandations OWASP.

  • Le nouveau formulaire AdminUserCreationForm et le formulaire existant AdminPasswordChangeForm permettent dorénavant de désactiver l’authentification par mot de passe en définissant un mot de passe inutilisable lors de l’enregistrement du formulaire. Cette fonctionnalité est accessible sur le site d’administration sur les pages de création d’utilisateur et de modification de mot de passe.

  • Les décorateurs login_required(), permission_required() et user_passes_test() prennent maintenant en charge l’enveloppement de fonctions de vue asynchrones.

  • ReadOnlyPasswordHashWidget inclut dorénavant un bouton pour réinitialiser le mot de passe de l’utilisateur, qui remplace le lien précédemment imbriqué dans le texte d’aide de ReadOnlyPasswordHashField, ce qui améliore l’accessibilité générale du formulaire UserChangeForm.

django.contrib.gis

  • BoundingCircle est dorénavant pris en charge avec SpatiaLite 5.1+.

  • Collect est dorénavant prise en charge avec MySQL 8.0.24+.

  • GeoIP2 permet maintenant de faire des requêtes avec des objets ipaddress.IPv4Address ou ipaddress.IPv6Address.

  • GeoIP2.country() expose désormais les valeurs continent_code, continent_name et is_in_european_union.

  • GeoIP2.city() expose désormais les valeurs accuracy_radius et region_name. De plus, les valeurs dma_code et region sont désormais exposées sous metro_code et region_code, mais les noms précédents sont aussi conservés par rétrocompatibilité.

  • Area prend désormais en charge l’unité ha (hectares).

  • Le nouvel attribut OGRGeometry.is_3d permet de contrôler si une géométrie possède une coordonnée de dimension Z.

  • La nouvelle méthode OGRGeometry.set_3d() permet l’ajout et la suppression de la coordonnée de dimension Z.

  • OGRGeometry, Point, LineString, Polygon et GeometryCollection et leurs sous-classes prennent dorénavant en charge les géométries avec mesures via les nouvelles propriétés OGRGeometry.is_measured et m, ainsi que la méthode OGRGeometry.set_measured().

  • OGRGeometry.centroid est maintenant disponible pour tous les types d’objets géométriques pris en charge.

  • Les fonctions FromWKB() et FromWKT() prennent désormais en charge l’argument facultatif srid (sauf pour Oracle où cet argument est ignoré).

django.contrib.postgres

  • BTreeIndex prend désormais en charge le paramètre deduplicate_items.

django.contrib.sessions

Moteurs de base de données

  • L’option "init_command" est dorénavant prise en charge dans les OPTIONS de SQLite pour permettre d’indiquer des options pragma à définir à l’établissement des connexions.

  • L’option "transaction_mode" est dorénavant prise en charge dans les OPTIONS de SQLite pour permettre d’indiquer le Comportement des transactions.

  • L’option "pool" est dorénavant prise en charge dans les OPTIONS de PostgreSQL pour permettre d’utiliser les réserves de connexions.

Signalement d’erreurs

  • Afin d’améliorer l’accessibilité, les pages d’erreur 404 et 500 utilisent dorénavant les éléments de positionnement HTML pour l’en-tête, le pied de page et les principales zones de contenus.

Stockage de fichier

Formulaires

  • Dans l’optique d’améliorer l’accessibilité et de permettre aux lecteurs d’écran d’associer les groupes de champs avec leur texte d’aide, les groupes de champs de formulaires incluent dorénavant l’attribut HTML aria-describedby.

Commandes d’administration

Migrations

  • Le nouvel attribut Operation.category permet d’indiquer une catégorie d'opération utilisée par la commande makemigrations pour afficher un symbole significatif de l’opération.

Modèles

Gabarits

  • Des balises personnalisées peuvent maintenant définir des données supplémentaires sur l’objet Parser qui seront ensuite disponibles sur l’instance Template. De telles données peuvent par exemple être utilisées par le chargeur de gabarits, ou d’autres clients des gabarits.

  • Les moteurs de gabarits implémentent dorénavant une méthode check() qui est déjà inscrite dans l’infrastructure des contrôles.

Tests

  • Les assertions assertContains(), assertNotContains() et assertInHTML() ajoutent maintenant les contenus recherchés aux messages d’erreur des assertions.

  • Les classes RequestFactory, AsyncRequestFactory, Client et AsyncClient prennent désormais en charge le paramètre query_params qui accepte un dictionnaire de clés/valeurs de chaînes de requête. Cela permet de définir plus facilement des chaînes de requête pour toute méthode HTTP.

    self.client.post("/items/1", query_params={"action": "delete"})
    await self.async_client.post("/items/1", query_params={"action": "delete"})
    
  • La nouvelle assertion SimpleTestCase.assertNotInHTML() permet de tester qu’un fragment HTML n’est pas contenu dans le contenu de recherche HTML donné.

  • Dans le but de renforcer l’isolation des tests, les connexions de base de données à l’intérieur des fils d’exécution ne sont plus autorisées dans SimpleTestCase.

Validateurs

Changements incompatibles avec les anciennes versions dans Django 5.1

django.contrib.gis

  • La prise en charge de PostGIS 2.5 a été supprimée.

  • La prise en charge de PROJ < 6 a été supprimée.

  • La prise en charge de GDAL 2.4 a été supprimée.

  • GeoIP2 n’ouvre plus à la fois les bases de données city et country lorsque un chemin vers un répertoire est indiqué, préférant la base de données city quand elle est disponible. La base de données des pays est un sous-ensemble de la base de données des villes et il n’y a souvent pas besoin des deux. Si vous avez vraiment besoin de la base de données des pays se trouvant dans le même répertoire de celle des villes, passez explicitement le chemin vers la base de données des pays dans le constructeur.

Abandon de la prise en charge de MariaDB 10.4

La prise en charge amont de MariaDB 10.4 se termine en juin 2024. Django 5.1 prend en charge MariaDB 10.5 et plus récent.

Abandon de la prise en charge de PostgreSQL 12

La prise en charge amont de PostgreSQL 12 se termine en novembre 2024. Django 5.1 prend en charge PostgreSQL 13 et plus récent.

Divers

  • Afin d’améliorer l’accessibilité, le filtre de la liste pour modification du site d’administration est produit dans une balise <nav> au lieu d’une <div>.

  • Afin d’améliorer l’accessibilité, le pied de page du site d’administration est produit dans une balise <footer> au lieu d’une <div>, et il a aussi été déplacé au-dessous de l’élément <div id="main">.

  • Afin d’améliorer l’accessibilité, le composant d’extension utilisé pour les groupes de champs ModelAdmin.fieldsets et InlineModelAdmin.fieldsets quand le groupe est nommé et utilise la classe collapse, contient désormais les balises <details> et <summary>.

  • Le fichier JavaScript collapse.js a été supprimé car il n’est plus nécessaire au site d’administration de Django.

  • SimpleTestCase.assertURLEqual() et assertInHTML() ajoutent maintenant ": " à msg_prefix, par cohérence avec le comportement des autres assertions.

  • django.utils.text.Truncator utilisé par les filtres de gabarit truncatechars_html et truncatewords_html utilisent dorénavant des sous-classes de html.parser.HTMLParser. Le résultat en est plus robuste et rapide, mais il se peut qu’il y ait de petites différences dans le résultat obtenu.

  • La fonction non documentée django.urls.converters.get_converter() est supprimée.

  • La version minimum de SQLite prise en charge est passée de 3.27.0 à 3.31.0.

  • FileField génère dorénavant une exception FieldError lors de l’enregistrement d’un fichier sans nom name.

  • ImageField.update_dimension_fields(force=True) n’est plus appelée après l’enregistrement de l’image dans le stockage. Si votre moteur de stockage redimensionne les images, les champs width_field et height_field ne correspondront pas à la largeur et la hauteur de l’image.

  • La version minimum de asgiref prise en charge est passée de 3.7.0 à 3.8.1.

  • To improve performance, the delete_selected admin action now uses QuerySet.bulk_create() when creating multiple LogEntry objects. As a result, pre_save and post_save signals for LogEntry are not sent when multiple objects are deleted via this admin action.

Fonctionnalités rendues obsolètes dans Django 5.1

Divers

  • Les méthodes ModelAdmin.log_deletion() et LogEntryManager.log_action() sont obsolètes. Ce sont les sous-classes qui doivent implémenter ces deux méthodes.

  • La fonction non documentée django.utils.itercompat.is_iterable() et le module django.utils.itercompat sont obsolètes. Utilisez isinstance(…, collections.abc.Iterable)` à la place.

  • La méthode django.contrib.gis.geoip2.GeoIP2.coords() est obsolète. Utilisez django.contrib.gis.geoip2.GeoIP2.lon_lat() à la place.

  • La méthode django.contrib.gis.geoip2.GeoIP2.open() est obsolète. Utilisez le constructeur GeoIP2 à la place.

  • La transmission d’arguments positionnels à Model.save() et Model.asave() a été rendue obsolète en faveur des arguments nommés.

  • L’accès en écriture à django.contrib.gis.gdal.OGRGeometry.coord_dim est obsolète. Utilisez set_3d() à la place.

  • La surcharge de convertisseurs existants avec django.urls.register_converter() est obsolète.

  • Le paramètre nommé check de CheckConstraint est obsolète, ce paramètre doit s’appeler dorénavant condition.

  • La propriété non documentée OS_OPEN_FLAGS de FileSystemStorage est obsolète. Pour permettre l’écrasement de fichiers dans un stockage, définissez la nouvelle option allow_overwrite à True.

  • La méthode get_cache_name() de FieldCacheMixin est obsolète en faveur de la propriété en cache cache_name.

Fonctionnalités supprimées dans 5.1

Ces fonctionnalités ont atteint la fin de leur cycle d’obsolescence et sont supprimées dans Django 5.1.

Voir Fonctionnalités rendues obsolètes dans Django 4.2 pour les détails de ces changements, ainsi que pour savoir comment supprimer l’utilisation de ces fonctionnalités.

  • La méthode BaseUserManager.make_random_password() est supprimée.

  • L’option de modèle Meta.index_together est supprimée.

  • Le filtre de gabarit length_is est supprimé.

  • Les classes django.contrib.auth.hashers.SHA1PasswordHasher, django.contrib.auth.hashers.UnsaltedSHA1PasswordHasher et django.contrib.auth.hashers.UnsaltedMD5PasswordHasher sont supprimées.

  • Les modèles django.contrib.postgres.fields.CICharField, django.contrib.postgres.fields.CIEmailField et django.contrib.postgres.fields.CITextField sont supprimés, sauf pour leur prise en charge dans les migrations historiques.

  • La classe mixin django.contrib.postgres.fields.CIText est supprimée.

  • Les attributs map_width et map_height de BaseGeometryWidget sont supprimés.

  • La méthode SimpleTestCase.assertFormsetError() est supprimée.

  • La méthode TransactionTestCase.assertQuerysetEqual() est supprimée.

  • La prise en charge des chaînes littérales encodées en JSON à JSONField et aux requêtes et expressions associées est supprimée.

  • La prise en charge des arguments positionnels à Signer et TimestampSigner est supprimée.

  • Les réglages DEFAULT_FILE_STORAGE et STATICFILES_STORAGE sont supprimés.

  • La fonction django.core.files.storage.get_storage_class() est supprimée.