Simplifier le développement web dynamique avec Django et HTMX : Utilisation des Forms et des Serializers

Dans notre précédent article, nous avons exploré comment HTMX transmet les données au serveur.

Lire l'article précédent : Simplifier le développement web dynamique avec Django et HTMX (Partie 4) : Méthodes de transmission des payloads

Alors que la fonction fetch() de JavaScript crée et envoie directement des payloads JSON, nous avons constaté que HTMX se rapproche davantage de la méthode consistant à collecter les valeurs du DOM et à les envoyer comme des données de formulaire.

Cela soulève naturellement la question suivante :

"Quelle est la manière la plus naturelle de valider les données reçues via HTMX dans Django ?"

Au premier abord, beaucoup pourraient penser aux DRF Serializers. Il est vrai que les Serializers sont des outils de validation puissants, utilisables même sans JSON. Cependant, lorsqu'on les utilise avec HTMX, on peut ressentir une certaine inadéquation.

Pourquoi ?

La raison est simple : le flux de travail de base de HTMX est plus proche de l'univers des formulaires HTML, et Django dispose déjà d'un objet Form conçu spécifiquement pour cet univers.

Dans cet article, nous allons explorer comment utiliser Django Form et DRF Serializer pour gérer les requêtes HTMX, et quelle approche est la plus naturelle et pratique.

Comparaison des rôles de Form et Serializer dans le traitement des requêtes HTMX


Les données envoyées par HTMX sont finalement proches des "données de formulaire"

Comme nous l'avons vu dans le précédent article, HTMX collecte et envoie les valeurs des éléments HTML au serveur. Cela peut se faire en envoyant les valeurs d'entrée d'un <form>, en incluant des éléments spécifiques avec hx-include, ou en ajoutant des valeurs supplémentaires avec hx-vals.

En d'autres termes, la philosophie de base de HTMX est la suivante :

  • Ne pas assembler directement d'objets JavaScript
  • Collecter les valeurs à partir des éléments HTML
  • Envoyer des requêtes au serveur
  • Mieux s'harmonise avec les réponses de fragments HTML qu'avec le JSON

Ce flux est bien plus important qu'il n'y paraît. Car cette structure correspond presque parfaitement au flux de traitement typique des formulaires Django.

Les formulaires Django sont également conçus avec les prémisses suivantes :

  • L'utilisateur saisit des données dans un formulaire HTML
  • Le serveur reçoit request.POST
  • Le Form valide les données
  • En cas d'erreur, le formulaire est rendu à nouveau
  • Si aucune erreur, les données sont sauvegardées et une réponse est envoyée

À ce stade, on commence à avoir l'impression que HTMX et les formulaires Django s'emboîtent parfaitement, comme Cendrillon et sa pantoufle de verre.

Nous pouvons donc en tirer la conclusion suivante :

L'outil de validation de Django qui s'harmonise le plus naturellement avec HTMX est le Django Form, plutôt que le DRF Serializer.


Pourquoi Django Form convient-il mieux ?

Les DRF Serializers sont sans aucun doute d'excellents outils. Cependant, si l'on considère leur contexte de conception original, les Serializers sont davantage des outils pour la sérialisation des données et la validation des entrées d'API.

En revanche, les formulaires Django ont été conçus dès le départ pour :

  • Traitement des entrées de formulaires HTML
  • Validation côté serveur
  • Affichage des messages d'erreur
  • Maintien des valeurs saisies
  • Re-rendu des templates

En d'autres termes, ils s'accordent beaucoup plus naturellement avec une approche comme HTMX, qui consiste à "recevoir une partie du HTML et la remplacer".

Prenons un exemple.

L'utilisateur soumet un formulaire de commentaire.

  • Si la validation échoue ?
  • Les données saisies doivent être conservées
  • Il faut indiquer quels champs posent problème
  • Le formulaire avec les erreurs doit être partiellement rendu à nouveau

Django Form excelle dans ce type d'expérience utilisateur.

Bien sûr, les Serializers fournissent également des errors et permettent la validation. Mais pour l'étape suivante, qui consiste à "reconstruire toute l'expérience utilisateur du formulaire HTML", l'approche Form est bien plus fluide.

Ainsi, en termes de compatibilité avec HTMX, il est juste de considérer le Form comme le choix par défaut.


La combinaison la plus naturelle : HTMX + Django Form

Commençons par l'exemple le plus simple : un formulaire d'enregistrement de tâche.

Définition du Formulaire

from django import forms

class TodoForm(forms.Form):
    title = forms.CharField(max_length=100, label="Titre")
    priority = forms.IntegerField(min_value=1, max_value=5, label="Priorité")

Dans la vue, il suffit maintenant de passer request.POST directement au Form pour la validation.

Traitement de la Vue

from django.shortcuts import render
from django.http import HttpResponse

def todo_create(request):
    if request.method == "POST":
        form = TodoForm(request.POST)
        if form.is_valid():
            title = form.cleaned_data["title"]
            priority = form.cleaned_data["priority"]

            # Exécuter la logique de sauvegarde
            # Todo.objects.create(title=title, priority=priority)

            return render(request, "todos/partials/todo_item.html", {
                "title": title,
                "priority": priority,
            })

        return render(request, "todos/partials/todo_form.html", {
            "form": form,
        }, status=400)

    form = TodoForm()
    return render(request, "todos/partials/todo_form.html", {
        "form": form,
    })

Les points clés ici sont très clairs :

  1. Recevoir les valeurs envoyées par HTMX via request.POST
  2. Le Form valide les données
  3. En cas de succès, renvoyer un fragment HTML (partial)
  4. En cas d'échec, rendre à nouveau le Formulaire contenant les erreurs

Ce flux est à la fois "Django-esque", "HTMX-esque", et surtout, facile à maintenir.


Afficher naturellement les erreurs dans le template

C'est là que les formulaires Django brillent particulièrement.

En cas d'échec de validation, l'objet Form contient déjà les informations suivantes :

  • Les valeurs saisies par l'utilisateur
  • Les messages d'erreur par champ
  • Les erreurs non liées à un champ
  • L'état de validité de chaque champ

Ainsi, le template partiel peut simplement les rendre tels quels.

todo_form.html

<form hx-post="{% url 'todo_create' %}" hx-target="#todo-form-wrap" hx-swap="outerHTML">
    {% csrf_token %}

    {% if form.non_field_errors %}
        <div class="error-box">
            {{ form.non_field_errors }}
        </div>
    {% endif %}

    <div>
        <label for="{{ form.title.id_for_label }}">Titre</label>
        {{ form.title }}
        {% if form.title.errors %}
            <div class="field-error">{{ form.title.errors }}</div>
        {% endif %}
    </div>

    <div>
        <label for="{{ form.priority.id_for_label }}">Priorité</label>
        {{ form.priority }}
        {% if form.priority.errors %}
            <div class="field-error">{{ form.priority.errors }}</div>
        {% endif %}
    </div>

    <button type="submit">Enregistrer</button>
</form>

Cet exemple est très simple, mais il illustre bien la synergie entre HTMX et les formulaires Django.

  • En cas d'échec, le formulaire entier peut être rendu à nouveau
  • Les valeurs saisies par l'utilisateur sont conservées
  • Les messages d'erreur s'affichent naturellement à côté des champs

Avec l'approche traditionnelle fetch() + JSON + manipulation manuelle du DOM, il aurait fallu écrire beaucoup de JavaScript pour implémenter un tel flux. Cependant, la combinaison HTMX et Django Form permet de gérer cela de manière beaucoup plus simple, en utilisant uniquement le code côté serveur et les templates.


Alors, les Serializers sont-ils inutiles ?

Dire que les Serializers sont inutiles serait exagéré. Il serait plus juste de dire : Il n'est pas nécessaire de choisir les Serializers par défaut.

En réalité, les Serializers de DRF restent tout à fait attrayants dans les situations suivantes :

  • Si DRF est déjà largement utilisé dans l'ensemble du projet
  • Si vous souhaitez réutiliser la même logique de validation pour les API et les écrans rendus côté serveur
  • Si la logique de validation des entrées est complexe et déjà bien organisée dans un Serializer
  • Si vous prévoyez d'exposer la même fonctionnalité à des applications mobiles ou des API externes ultérieurement

En d'autres termes, la question n'est pas "Peut-on utiliser un Serializer avec HTMX ?", mais plutôt "Est-ce que l'intégration d'un Serializer ici apporte un avantage significatif à l'architecture globale ?"


Il est également possible d'utiliser un Serializer

Par exemple, imaginons que vous ayez déjà un Serializer comme celui-ci :

from rest_framework import serializers

class TodoSerializer(serializers.Serializer):
    title = serializers.CharField(max_length=100)
    priority = serializers.IntegerField(min_value=1, max_value=5)

Dans ce cas, il peut tout à fait être utilisé pour les requêtes HTMX.

from django.shortcuts import render

def todo_create_with_serializer(request):
    if request.method == "POST":
        serializer = TodoSerializer(data=request.POST)

        if serializer.is_valid():
            title = serializer.validated_data["title"]
            priority = serializer.validated_data["priority"]

            return render(request, "todos/partials/todo_item.html", {
                "title": title,
                "priority": priority,
            })

        return render(request, "todos/partials/todo_form_serializer.html", {
            "errors": serializer.errors,
            "data": request.POST,
        }, status=400)

Comme vous pouvez le voir, techniquement, il n'y a aucun problème. Les Serializers ne sont pas uniquement destinés à recevoir du JSON.

Cependant, une différence subtile apparaît ici.

Lorsque vous utilisez un Form :

  • Maintien des valeurs saisies
  • Rendu par champ
  • Liaison des erreurs
  • Connexion avec le template

Cela s'enchaîne naturellement.

En revanche, lorsque vous utilisez un Serializer :

  • Il faut décomposer serializer.errors manuellement pour l'adapter à la structure du template
  • Les valeurs d'entrée existantes doivent être passées séparément
  • Le développeur doit accorder plus d'attention à la connexion avec le re-rendu du formulaire HTML

En d'autres termes, il est utilisable, mais cela implique un peu plus de travail manuel.

C'est précisément à cause de cela que l'utilisation d'un Serializer avec HTMX peut sembler un peu forcée.


Conclusion

Dans le précédent article, nous avons examiné comment HTMX envoie les données. Dans celui-ci, nous avons résumé la manière la plus naturelle de valider ces données dans Django.

  • HTMX s'harmonise naturellement avec les données de formulaire
  • Django Form est l'outil conçu précisément pour ce flux
  • Par conséquent, en termes de compatibilité avec HTMX, le Form est le choix le plus naturel
  • Le DRF Serializer est utilisable, mais il s'agit plutôt d'un choix stratégique

Personnellement, je pense que pour exploiter pleinement HTMX, il est nécessaire de retrouver non seulement le "sens de la manipulation d'AJAX de manière HTML-esque", mais aussi le "sens de l'utilisation des formulaires et des balises de formulaire Django".


Articles connexes