J'ai quelques difficultés à trouver le meilleur endroit (lire : DRY et maintenable) pour introduire une logique de validation dans Django, notamment entre les modèles, les formulaires et les sérialiseurs DRF.
Je travaille avec Django depuis plusieurs années et j'ai suivi les différentes conventions pour gérer la validation des modèles, des formulaires et des points de terminaison de l'API REST. J'ai essayé de nombreuses variantes pour assurer l'intégrité globale des données, mais j'ai récemment rencontré une petite pierre d'achoppement. Voici une brève liste de ce que j'ai essayé après avoir parcouru de nombreux articles, messages de l'OS et tickets :
-
Validation au niveau du modèle ; à savoir, s'assurer que toutes mes contraintes personnalisées correspondent avant d'appeler
myModel.save()
en passant outremyModel.clean()
(ainsi que des méthodes communes spécifiques au domaine et uniques). Pour ce faire, j'ai veillé àmyModel.full_clean()
a été appelé enmyForm.clean()
(pour les formulaires -- et le panneau d'administration le fait déjà) etmySerializer.validate()
(pour les sérialiseurs DRF). -
Validation au niveau du formulaire et du sérialiseur, en faisant appel à une méthode partagée pour un code maintenable et DRY.
-
Validation au niveau du formulaire et du sérialiseur, avec une méthode distincte pour chacun d'eux afin de garantir une flexibilité maximale (par exemple, lorsque les formulaires et les terminaux ont des contraintes différentes).
La première méthode me semble la plus intuitive lorsque les formulaires et les sérialiseurs ont des contraintes identiques, mais elle est un peu désordonnée en pratique ; d'abord, les données sont automatiquement nettoyées et validées par le formulaire ou le sérialiseur, puis l'entité du modèle est instanciée, et une nouvelle validation est exécutée - ce qui est un peu alambiqué et peut devenir compliqué.
La troisième méthode est celle que Django Rest Framework recommande à partir de la version 3.0 ; ils ont éliminé une grande partie de leur système de gestion de l'information. model.save()
et préfèrent laisser la validation aux aspects de l'application destinés aux utilisateurs. Cela me semble logique, puisque la base de Django model.save()
n'appelle pas model.full_clean()
de toute façon.
Ainsi, la méthode deux me semble être le meilleur résultat global généralisé ; la validation vit dans un endroit distinct -- avant que le modèle ne soit jamais touché -- et la base de code est moins encombrée / plus DRY en raison de la logique de validation partagée.
Malheureusement, la plupart des problèmes que j'ai rencontrés sont liés à la coopération des sérialiseurs de Django Rest Framework. Les trois approches fonctionnent bien pour les formulaires, et en fait pour la plupart des méthodes HTTP (notamment lors de la création d'une entité par POST), mais aucune ne semble fonctionner correctement lors de la mise à jour d'une entité existante (PUT, PATCH).
Pour faire court, il s'est avéré assez difficile de valider les données entrantes lorsqu'elles sont incomplètes (mais par ailleurs valides - c'est souvent le cas pour PATCH). Les données de la demande peuvent ne contenir que certains champs -- ceux qui contiennent des informations différentes/nouvelles -- et les informations existantes de l'instance du modèle sont maintenues pour tous les autres champs. En fait, Numéro 4306 de la DRF résume parfaitement ce défi particulier.
J'ai également envisagé d'exécuter la validation du modèle personnalisé au niveau du viewset (après le remplissage de serializer.validated_data et l'existence de serializer.instance, mais avant l'appel de serializer.save()), mais j'ai encore du mal à trouver une approche propre et généralisée en raison de la complexité du traitement des mises à jour.
TL;DR Le cadre Django Rest rend un peu difficile l'écriture d'une logique de validation propre et maintenable à un endroit évident, en particulier pour les mises à jour partielles qui reposent sur un mélange de données de modèle existantes et de données de requête entrantes.
J'aimerais que des gourous de Django nous disent ce qu'ils ont réussi à faire, car je ne vois pas de solution pratique.
Gracias.