Notes de publication de Django 3.0

2 décembre 2019

Bienvenue dans Django 3.0 !

Ces notes de publications couvrent les nouvelles fonctionnalités, ainsi que certaines modifications non rétro-compatibles dont il faut être au courant lors la mise à jour depuis Django 2.2 ou des versions plus anciennes. Nous avons abandonné certaines fonctionnalités qui ont atteint la fin de leur cycle d’obsolescence et 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 3.0 requiert Python 3.6, 3.7, 3.8 ou 3.9 (dès 3.0.11). Nous recommandons vivement et nous ne prenons officiellement en charge que la dernière publication de chaque série.

La série Django 2.2.x est la dernière à prendre en charge Python 3.5.

Prise en charge des bibliothèques tierces pour les anciennes versions de Django

Après la publication de Django 3.0, nous suggérons aux applications tierces de ne plus prendre en charge les versions de Django plus anciennes que 2.2. À ce moment, vous devriez pouvoir exécuter les tests de votre projet en utilisant python -Wd afin de faire apparaître les avertissements d’obsolescence. Après avoir corrigé ceux-ci, votre application devrait être compatible avec Django 3.0.

Quoi de neuf dans Django 3.0

Prise en charge de MariaDB

Django prend dorénavant officiellement en charge MariaDB 10.1 ou plus récent. Lisez les notes MariaDB pour plus de détails.

Prise en charge d’ASGI

Django 3.0 commence le voyage vers la fonctionnalité asynchrone de Django en fournissant la prise en charge de l’exécution d’applications ASGI.

Il s’agit d’un ajout en plus de la prise en charge existante du protocole WSGI. Django prévoit de prendre en charge les deux à l’avenir. Les fonctionnalités asychrones ne seront toutefois disponibles que pour les applications fonctionnant avec ASGI.

At this stage async support only applies to the outer ASGI application. Internally everything remains synchronous. Asynchronous middleware, views, etc. are not yet supported. You can, however, use ASGI middleware around Django’s application, allowing you to combine Django with other ASGI frameworks.

Aucun besoin de convertir vos applications sauf si vous souhaitez expérimenter avec du code asynchrone ; si vous souhaitez en savoir plus, vous pouvez lire la documentation sur le déploiement avec ASGI.

Notez qu’un effet de bord de cette nouveauté est que Django sait maintenant détecter des boucles événementielles asynchrones et vous empêchera d’appeler du code marqué comme non adapté à l’asynchrone (« async unsafe »), tel que des opérations avec l’ORM, à partir d’un contexte asynchrone. Si vous utilisiez précédemment Django à partir de code asynchrone, ceci pourrait vous bloquer si vous ne le faisiez pas correctement. Si vous voyez une erreur SynchronousOnlyOperation, examinez attentivement votre code et déplacez d’éventuelles opérations de base de données plutôt dans un fil d’exécution synchrone.

Contraintes d’exclusion avec PostgreSQL

La nouvelle classe ExclusionConstraint permet d’ajouter des contraintes d’exclusion avec PostgreSQL Ces contraintes peuvent être ajoutées aux modèles en utilisant l’option Meta.constraints.

Expressions de filtres

Les expressions qui produisent des résultats BooleanField peuvent dorénavant être directement utilisées dans des filtres QuerySet, sans devoir passer par une annotation préalable et un filtre sur cette annotation.

Énumérations pour les choix de champs de modèles

Les types d’énumération personnalisés TextChoices, IntegerChoices et Choices sont dorénavant disponibles dans l’optique de définir Field.choices. Les types TextChoices et IntegerChoices sont fournis pour les champs texte et nombre entier. La classe Choices permet de définir une énumération compatible avec les autres types de données concrets. Ces types personnalisés prennent en charge les étiquettes conviviales pouvant être traduites et obtenues par une propriété de l’énumération ou de ses membres. Lisez Types énumératifs pour plus de détails et des exemples.

Fonctionnalités mineures

django.contrib.admin

  • La prise en charge de l’attribut admin_order_field pour les propriétés dans ModelAdmin.list_display.
  • La nouvelle méthode ModelAdmin.get_inlines() permet de définir les sous-formulaires intégrés sur la base de la requête ou de l’instance de modèle.
  • La bibliothèque Select2 a été mise à jour de la version 4.0.3 vers 4.0.7.
  • La version intégrée de jQuery a été mise à jour de 3.3.1 vers 3.4.1.

django.contrib.auth

  • Le nouvel attribut reset_url_token dans PasswordResetConfirmView permet de définir un paramètre jeton affiché comme composant des URL de réinitialisation de mot de passe.
  • La classe BaseBackend a été ajoutée pour faciliter la personnalisation des moteurs d’authentification.
  • La méthode get_user_permissions() a été ajoutée pour faire écho à la méthode existante get_group_permissions().
  • L’attribut HTML autocomplete a été ajouté aux composants des champs nom d’utilisateur, courriel et mot de passe dans django.contrib.auth.forms pour une meilleure interaction avec les gestionnaires de mots de passe des navigateurs.
  • createsuperuser se replie sur les variables d’environnement pour les champs mot de passe et autres champs obligatoires, lorsqu’elle ne trouve pas de paramètre correspondant sur la ligne de commande en mode non interactif.
  • REQUIRED_FIELDS prend dorénavant en charge les champs ManyToManyField.
  • La nouvelle méthode UserManager.with_perm() renvoie les utilisateurs ayant la permission indiquée.
  • Le nombre d’itération par défaut du hacheur de mot de passe PBKDF2 a été augmenté de 150’000 à 180’000.

django.contrib.gis

  • Les fonctions de requête spatiale MySQL opèrent dorénavant sur les géométries réelles. Précédemment, seules les boîtes englobantes étaient prises en compte.
  • La fonction GeometryDistance a été ajoutée, prise en charge par PostGIS.
  • La prise en charge de l’unité furlong (sillon) a été ajoutée dans Distance.
  • Le réglage GEOIP_PATH accepte maintenant les chemins pathlib.Path.
  • La classe GeoIP2 accepte maintenant les chemins pathlib.Path.

django.contrib.postgres

django.contrib.sessions

django.contrib.syndication

Cache

Stockage de fichier

  • La nouvelle méthode Storage.get_alternative_name() permet d’adapter l’algorithme de génération des noms de fichiers lorsque le nom d’un fichier téléversé existe déjà.

Formulaires

Internationalisation

Journalisation

  • Le nouveau paramètre reporter_class de AdminEmailHandler permet de fournir une sous-classe de django.views.debug.ExceptionReporter pour personnaliser le texte de trace d’erreur envoyé aux ADMINS du site lorsque DEBUG vaut False.

Commandes d’administration

  • La nouvelle option compilemessages --ignore permet d’ignorer des répertoires spécifiques lors de la recherche de fichiers .po à compiler.
  • showmigrations --list affiche dorénavant la date d’application lorsque --verbosity est de 2 ou plus.
  • Avec PostgreSQL, dbshell prend dorénavant en charge les certificats TLS client.
  • inspectdb sait dorénavant découvrir les champs OneToOneField lorsqu’une clé étrangère possède une contrainte unique ou de clé primaire.
  • La nouvelle option --skip-checks omet l’exécution des contrôles système avant de lancer la commande.
  • Les options startapp --template et startproject --template prennent maintenant en charge les gabarits stockés dans des archives XZ (.tar.xz, .txz) et LZMA (.tar.lzma, .tlz).

Modèles

  • Les fonctions de base de données de génération d’empreintes MD5, SHA1, SHA224, SHA256, SHA384 et SHA512 ont été ajoutées.

  • La fonction de base de données Sign a été ajoutée.

  • Le nouveau paramètre is_dst des fonctions de base de données Trunc détermine le traitement des heures non existantes ou ambiguës.

  • connection.queries affiche dorénavant les instructions COPY TO avec PostgreSQL.

  • FilePathField accepte dorénavant un objet exécutable pour path.

  • Les tables intermédiaires symétriques sont permises pour les champs auto-référencés ManyToManyField.

  • Les attributs name de CheckConstraint, UniqueConstraint et Index prennent dorénavant en charge l’interpolation de classe et d’étiquette d’application en utilisant les substituants '%(app_label)s' et '%(class)s'.

  • Le nouvel attribut Field.descriptor_class permet aux champs de modèles de personnaliser le comportement get et set en surchargeant leurs descripteurs.

  • Avg et Sum acceptent dorénavant un paramètre distinct.

  • Le champ SmallAutoField a été ajouté ; il est similaire à AutoField sauf qu’il ne permet des valeurs que jusqu’à une certaine limite (dépendante de la base de données). Les valeurs de 1 à 32767 sont valables pour toutes les bases de données prises en charge par Django.

  • AutoField, BigAutoField et SmallAutoField héritent dorénavant respectivement de IntegerField, BigIntegerField et SmallIntegerField. Les contrôles système et les validateurs sont aussi correctement hérités.

  • FileField.upload_to accepte maintenant les chemins pathlib.Path.

  • CheckConstraint est dorénavant prise en charge avec MySQL 8.0.16+.

  • La nouvelle méthode allows_group_by_selected_pks_on_model() de django.db.backends.base.BaseDatabaseFeatures permet d’optimiser les clauses GROUP BY pour n’exiger que les clés primaires des modèles sélectionnés. Par défaut, elle n’est prise en charge que pour les modèles gérés avec PostgreSQL.

    Pour activer l’optimisation de clé primaire seule pour GROUP BY pour les modèles non gérés (« unmanaged »), vous deez créer une sous-classe du moteur de base de données PostgreSQL, surchargeant la classe de fonctionnalité features avec une méthode allows_group_by_selected_pks_on_model() selon vos besoins. Voir l’exemple Sous-classes des moteurs de base de données intégrés.

Requêtes et réponses

  • HttpResponse peut maintenant être initialisée avec du contenu memoryview.
  • For use in, for example, Django templates, HttpRequest.headers now allows lookups using underscores (e.g. user_agent) in place of hyphens.

Sécurité

Tests

  • Le nouveau paramètre raise_request_exception de la classe de test Client permet de contrôler si les exceptions générées pendant la requête doivent être propagées dans les tests. La valeur par défaut est True pour des raisons de rétrocompatibilité. Quand elle vaut False et qu’une exception survient, le client de test renverra une réponse 500 avec l’attribut exc_info, un tuple fournissant des informations sur l’exception survenue.
  • Les tests et les cas de tests à lancer peuvent être choisis par un motif de nom de test à l’aide de la nouvelle option test -k.
  • La comparaison HTML, telle qu’utilisée par assertHTMLEqual() traite dorénavant le texte, les références de caractères et les références d’entité qui se réfèrent au même caractère comme équivalents.
  • Le lanceur de tests de Django prend dorénavant en charge le mode sans interface pour les tests Selenium avec les navigateurs pris en charge. Ajoutez l’option --headless pour activer ce mode.
  • Le lanceur de tests de Django prend dorénavant en charge les options --start-at et --start-after pour lancer les tests en commençant à partir d’un certain module de premier niveau.
  • Le lanceur de tests de Django prend dorénavant en charge une option --pdb pour déclencher un débogueur à chaque erreur ou échec.

Changements incompatibles avec les anciennes versions dans Django 3.0

Model.save() when providing a default for the primary key

Model.save() no longer attempts to find a row when saving a new Model instance and a default value for the primary key is provided, and always performs a single INSERT query. In older Django versions, Model.save() performed either an INSERT or an UPDATE based on whether or not the row exists.

This makes calling Model.save() while providing a default primary key value equivalent to passing force_insert=True to model’s save(). Attempts to use a new Model instance to update an existing row will result in an IntegrityError.

In order to update an existing model for a specific primary key value, use the update_or_create() method or QuerySet.filter(pk=…).update(…) instead. For example:

>>> MyModel.objects.update_or_create(pk=existing_pk, defaults={"name": "new name"})
>>> MyModel.objects.filter(pk=existing_pk).update(name="new name")

API de moteur de base de données

Cette section décrit des modifications qui pourraient être nécessaires dans des moteurs de base de données tiers.

  • Le second paramètre de DatabaseIntrospection.get_geometry_type() est dorénavant la description de la ligne au lieu du nom de la colonne.
  • DatabaseIntrospection.get_field_type() ne renvoie plus forcément des tuples.
  • Si une base de données peut créer des clés étrangères dans la même instruction SQL qui ajoute un champ, ajoutez l’attribut SchemaEditor.sql_create_column_inline_fk à son moteur, avec le contenu SQL approprié ; sinon, définissez DatabaseFeatures.can_create_inline_fk = False.
  • DatabaseFeatures.can_return_id_from_insert et can_return_ids_from_bulk_insert sont renommés en can_return_columns_from_insert et can_return_rows_from_bulk_insert.
  • Les fonctions de base de données prennent maintenant en charge les formats datetime.timezone lorsqu’elles sont créées avec des instances datetime.timedelta (par ex. timezone(timedelta(hours=5)), qui produirait 'UTC+05:00'). Les moteurs tiers doivent prendre en compte ce format lors de la préparation de DateTimeField dans datetime_cast_date_sql(), datetime_extract_sql(), etc.
  • Des lignes sont ajoutées à DatabaseOperations.integer_field_ranges pour les champs AutoField, BigAutoField et SmallAutoField afin de prendre en charge les validateurs d’intervalles de nombres entiers pour ces types de champ. Les moteurs tiers pourraient avoir à adapter les lignes par défaut.
  • DatabaseOperations.fetch_returned_insert_id() est remplacée par fetch_returned_insert_columns() qui renvoie une liste de valeurs renvoyées par l’instruction INSERT RETURNING au lieu d’une valeur unique.
  • DatabaseOperations.return_insert_id() est remplacée par return_insert_columns() qui accepte un paramètre fields qui est un itérable de champs à renvoyer après l’insertion. Généralement, il ne s’agit que du champ clé primaire auto-généré.

django.contrib.admin

  • Les messages de changement des historiques de modèle du site d’administration préfèrent dorénavant les étiquettes de champ plus lisibles plutôt que les noms de champ.

django.contrib.gis

  • La prise en charge de PostGIS 2.1 a été supprimée.
  • La prise en charge de SpatiaLite 4.1 et 4.2 a été supprimée.
  • La prise en charge de GDAL 1.11 et GEOS 3.4 a été supprimée.

Abandon de la prise en charge de PostgreSQL 9.4

La prise en charge amont de PostgreSQL 9.4 se termine en décembre 2019. Django 3.0 prend en charge PostgreSQL 9.5 et plus récent.

Abandon de la prise en charge de Oracle 12.1

La prise en charge de Oracle 12.1 par le projet amont se termine en juillet 2021. Django 2.2 sera pris en charge jusqu’en avril 2022. Django 3.0 prend officiellement en charge Oracle 12.2 et 18c.

Les API de compatibilité privées de Python 2 ont été supprimées.

Bien que la prise en charge de Python 2 ait été supprimée dans Django 2.0, certaines API privées n’ont pas été supprimées de Django afin que des applications tierces puissent continuer à les utiliser jusqu’à la fin de vie de Python 2.

Comme nous nous attendons à ce que les applications abandonnent la compatibilité avec Python 2 lors de l’ajout de la prise en charge de Django 3.0, nous supprimons ces API dans cette version.

  • django.test.utils.str_prefix() - Les chaînes n’ont plus de préfixe « u » avec Python 3.
  • django.test.utils.patch_logger() - Utilisez unittest.TestCase.assertLogs() à la place.
  • django.utils.lru_cache.lru_cache() - Alias de functools.lru_cache().
  • django.utils.decorators.available_attrs() - Cette fonction renvoie functools.WRAPPER_ASSIGNMENTS.
  • django.utils.decorators.ContextDecorator - Alias de contextlib.ContextDecorator.
  • django.utils._os.abspathu() - Alias de os.path.abspath().
  • django.utils._os.upath() et npath() - Ces fonctions ne font plus rien en Python 3.
  • django.utils.six - Remove usage of this vendored library or switch to six.
  • django.utils.encoding.python_2_unicode_compatible() - Alias de six.python_2_unicode_compatible().
  • django.utils.functional.curry() - Use functools.partial() or functools.partialmethod. See 5b1c389603a353625ae1603ba345147356336afb.
  • django.utils.safestring.SafeBytes - Inutilisée depuis Django 2.0.

Nouvelle valeur par défaut du réglage FILE_UPLOAD_PERMISSIONS

Dans les versions précédentes, le réglage FILE_UPLOAD_PERMISSIONS valait None par défaut. Avec le gestionnaire par défaut FILE_UPLOAD_HANDLERS, cela pouvait aboutir à des fichiers téléversés ayant des permissions différentes dépendant de leur taille et donc de quel gestionnaire de téléversement était utilisé.

FILE_UPLOAD_PERMISSIONS now defaults to 0o644 to avoid this inconsistency.

Nouvelles valeurs par défaut de réglages de sécurité

Pour rendre les projets Django plus sûrs par défaut, certains réglages de sécurité ont dorénavant des valeurs par défaut plus sûres :

Consultez la section sécurité des nouveautés de Django 3.0 pour plus de détails sur ces changements.

Divers

  • ContentType.__str__() inclut dorénavant l’étiquette app_label de son modèle pour distinguer les modèles ayant le même nom dans des applications différentes.
  • Comme l’accès à la langue dans la session a été rendu obsolète au profit des cookies, LocaleMiddleware ne cherche plus la langue de l’utilisateur dans la session et django.contrib.auth.logout() ne préserve plus la langue de la session après la déconnexion.
  • django.utils.html.escape() utilise dorénavant html.escape() pour l’échappement du code HTML. Le guillemet simple ' est dorénavant converti en ' au lieu de l’équivalent en format décimal '.
  • L’option django-admin test -k fonctionne dorénavant comme l’option unittest -k plutôt que comme un raccourci de --keepdb.
  • La prise en charge de pywatchman < 1.2.0 a été abandonnée.
  • urlencode() code dorénavant les valeurs itérables telles qu’elles sont lorsque doseq=False, plutôt que de les passer en boucle, ce qui rapproche du comportement de la fonction de bibliothèque standard urllib.parse.urlencode().
  • Le filtre de gabarit intword traduit dorénavant 1.0 avec une phrase au singulier et toutes les autres valeurs numériques comme une phrase au pluriel, ce qui n’est pas toujours correct selon les langues.
  • Assigning a value to a model’s ForeignKey or OneToOneField '_id' attribute now unsets the corresponding field. Accessing the field afterward will result in a query.
  • patch_vary_headers() traite dorénavant l’astérisque '*' en accord avec la RFC 7231#section-7.1.4, c’est-à-dire que si une liste de noms de champs d’en-tête contient un astérisque, l’en-tête Vary sera formé d’un seul astérisque '*'.
  • Avec MySQL 8.0.16+, PositiveIntegerField et PositiveSmallIntegerField incluent dorénavant une contrainte de contrôle pour empêcher les valeurs négatives dans la base de données.
  • alias=None a été ajouté à la signature de Expression.get_group_by_cols().
  • RegexPattern, used by re_path(), no longer returns keyword arguments with None values to be passed to the view for the optional named groups that are missing.

Fonctionnalités rendues obsolètes dans Django 3.0

django.utils.encoding.force_text() et smart_text()

Les alias smart_text() et force_text() (depuis Django 2.0) de smart_str() et force_str() sont rendus obsolètes. Vous pouvez ignorer l’avertissement si votre code prend en charge Python 2 car le comportement de smart_str() et force_str() est différent dans ce contexte.

Divers

  • django.utils.http.urlquote(), urlquote_plus(), urlunquote() et urlunquote_plus() sont rendus obsolètes en faveur des fonctions pour lesquelles elles sont des alias : urllib.parse.quote(), quote_plus(), unquote() et unquote_plus().
  • django.utils.translation.ugettext(), ugettext_lazy(), ugettext_noop(), ungettext() et ungettext_lazy() sont rendues obsolètes en faveur des fonctions pour lesquelles elles sont des alias : django.utils.translation.gettext(), gettext_lazy(), gettext_noop(), ngettext() et ngettext_lazy().
  • Afin de limiter la création de sessions et donc de favorise certaines stratégies de cache, django.views.i18n.set_language() arrêtera de définir la langue de l’utilisateur dans la session à partir de Django 4.0. Dès Django 2.1, la langue est toujours stockée dans le cookie LANGUAGE_COOKIE_NAME.
  • django.utils.text.unescape_entities() est rendu obsolète en faveur de html.unescape(). Notez qu’au contraire de unescape_entities(), html.unescape() évalue immédiatement les chaînes différées.
  • Pour éviter une possible confusion sur sa portée réelle, l’utilitaire interne privé is_safe_url() a été renommé en url_has_allowed_host_and_scheme(). Le fait que le protocole et l’hôte d’une URL soit autorisés n’implique en général pas qu’elle soit sûre. Elle pourrait par exemple être codée de façon incorrecte. Assurez-vous d’appliquer aussi iri_to_uri() sur la partie chemin des URL de source non fiable.

Fonctionnalités supprimées dans 3.0

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

Voir Features deprecated in 2.0 pour les détails de ces changements, ainsi que pour savoir comment supprimer l’utilisation de ces fonctionnalités.

  • Le module django.db.backends.postgresql_psycopg2 a été supprimé.
  • django.shortcuts.render_to_response() a été supprimée.
  • Le réglage DEFAULT_CONTENT_TYPE a été supprimé.
  • HttpRequest.xreadlines() a été supprimée.
  • La prise en charge du paramètre context de Field.from_db_value() et de Expression.convert_value() a été supprimée.
  • Le paramètre nommé field_name de QuerySet.earliest() et de latest() a été supprimé.

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

  • La fonction SIG ForceRHR a été supprimée.
  • django.utils.http.cookie_date() a été supprimée.
  • Les bibliothèques de balises de gabarits staticfiles et admin_static ont été supprimées.
  • django.contrib.staticfiles.templatetags.staticfiles.static() a été supprimée.
Back to Top