Dynamische Webentwicklung vereinfachen mit Django und HTMX: Einsatz von Forms und Serializern



Im letzten Artikel haben wir uns angesehen, wie HTMX Daten an den Server sendet.

Vergangener Artikel : Dynamische Webentwicklung mit Django und HTMX vereinfachen (Teil 4): Payload-Übertragungsarten

Wir haben festgestellt, dass HTMX dem Senden von Formulardaten durch das Sammeln von Werten aus dem DOM ähnlicher ist, während das herkömmliche fetch() von JavaScript eher dem direkten Erstellen und Senden von JSON-Payloads entspricht.

Da stellt sich natürlich die Frage:

"Womit lässt sich diese über HTMX empfangene Daten in Django am natürlichsten validieren?"

Anfangs denken viele vielleicht an den DRF Serializer. Tatsächlich ist der Serializer ein mächtiges Validierungstool und kann auch für Nicht-JSON-Daten verwendet werden. Doch bei der praktischen Anwendung mit HTMX fühlt es sich manchmal etwas erzwungen an.

Warum ist das so?

Der Grund ist einfach: Der grundlegende Ablauf von HTMX ist der Welt der HTML-Formulare ähnlicher, und Django bietet bereits Form-Klassen, die genau für diese Welt entwickelt wurden.

In diesem Artikel werden wir erläutern, wie Django Form und DRF Serializer jeweils bei der Verarbeitung von HTMX-Anfragen eingesetzt werden können und welche Option die natürlichere und praktischere Wahl darstellt.

Vergleich der Rollen von Form und Serializer bei der HTMX-Anfrageverarbeitung


Die von HTMX gesendeten Daten ähneln letztlich "Formulardaten"

Wie im letzten Teil erläutert, sammelt HTMX grundsätzlich Werte von HTML-Elementen und sendet sie an den Server. Dies geschieht entweder durch das Senden von Eingabewerten innerhalb eines <form>-Tags, das Einbeziehen bestimmter Elemente mit hx-include oder das Hinzufügen zusätzlicher Werte mit hx-vals.

Die Grundphilosophie von HTMX lässt sich wie folgt zusammenfassen:

  • Keine direkte Zusammenstellung von JavaScript-Objekten
  • Sammeln von Werten aus HTML-Elementen
  • Senden einer Anfrage an den Server
  • Besser geeignet für HTML-Fragmentantworten als für JSON

Dieser Ablauf ist wichtiger, als man vielleicht denkt. Denn diese Struktur entspricht nahezu dem typischen Django Form-Verarbeitungsablauf.

Auch Django Form wurde mit den folgenden Prämissen entworfen:

  • Der Benutzer gibt Daten in ein HTML-Formular ein.
  • Der Server empfängt request.POST.
  • Das Formular validiert die Daten.
  • Bei Fehlern wird neu gerendert.
  • Wenn keine Probleme vorliegen, werden die Daten gespeichert und ein Ergebnis zurückgegeben.

An diesem Punkt beginnt man zu spüren, dass HTMX und Django Form perfekt zusammenpassen, wie Aschenputtel und ihr Glasschuh.

Daher können wir wohl zu dem Schluss kommen:

Das Validierungstool in Django, das am natürlichsten mit HTMX harmoniert, ist Django Form und nicht der DRF Serializer.


Warum passt Django Form besser?



Der DRF Serializer ist zweifellos ein hervorragendes Werkzeug. Berücksichtigt man jedoch seinen ursprünglichen Designkontext, ist der Serializer eher ein Tool für Daten-Serialisierung und API-Eingabeprüfung.

Django Form hingegen wurde von Anfang an für Folgendes entwickelt:

  • Verarbeitung von HTML-Formulareingaben
  • Serverseitige Validierung
  • Anzeige von Fehlermeldungen
  • Beibehalten von Eingabewerten
  • Neu-Rendering von Templates

Das bedeutet, es fügt sich viel natürlicher ein, wenn es mit einem Ansatz wie HTMX kombiniert wird, bei dem "Teile des HTML-Codes empfangen und ausgetauscht werden."

Betrachten wir ein Beispiel:

Ein Benutzer sendet ein Kommentarformular.

  • Was passiert, wenn die Validierung fehlschlägt?
  • Die eingegebenen Inhalte sollten beibehalten werden.
  • Es sollte angezeigt werden, welche Felder problematisch sind.
  • Das Formular mit den Fehlern muss teilweise neu gerendert werden.

Solche UX-Anforderungen meistert Django Form hervorragend.

Natürlich bietet auch der Serializer errors und ermöglicht die Validierung. Aber der nächste Schritt, "die Neukonfiguration der gesamten HTML-Formular-UX", ist mit Forms wesentlich eleganter.

Daher ist es, wenn man nur die Kompatibilität mit HTMX betrachtet, korrekt, Form als die Standardoption anzusehen.


Die natürlichste Kombination: HTMX + Django Form

Schauen wir uns zunächst das grundlegendste Beispiel an: Ein einfaches To-Do-Registrierungsformular.

Form-Definition

from django import forms

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

Im View kann request.POST nun direkt in das Formular eingefügt und validiert werden.

View-Verarbeitung

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"]

            # Speicherlogik ausführen
            # 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,
    })

Die Kernpunkte hier sind sehr klar:

  1. Die von HTMX gesendeten Werte werden über request.POST empfangen.
  2. Das Formular validiert die Daten.
  3. Bei Erfolg wird ein HTML-Partial zurückgegeben.
  4. Bei Misserfolg wird das Formular mit den Fehlern neu gerendert.

Dieser Ablauf ist typisch für Django, typisch für HTMX und vor allem wartungsfreundlich.


Fehler im Template natürlich erneut anzeigen

Hier glänzt Django Form besonders.

Bei Validierungsfehlern enthält das Form-Objekt bereits folgende Informationen:

  • Die vom Benutzer eingegebenen Werte
  • Fehlermeldungen pro Feld
  • Nicht-Feld-Fehler
  • Der Status, welche Felder ungültig sind

Daher kann dies im Partial-Template einfach gerendert werden.

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 }}">Titel</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ät</label>
        {{ form.priority }}
        {% if form.priority.errors %}
            <div class="field-error">{{ form.priority.errors }}</div>
        {% endif %}
    </div>

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

Dieses Beispiel ist sehr einfach, zeigt aber gut die Kompatibilität von HTMX und Django Form.

  • Bei Misserfolg kann das gesamte Formular neu gerendert werden.
  • Die vom Benutzer eingegebenen Werte bleiben erhalten.
  • Fehlermeldungen werden natürlich neben den Feldern angezeigt.

Mit dem herkömmlichen Ansatz von fetch() + JSON + manueller DOM-Manipulation hätte man wahrscheinlich viel JavaScript benötigt, um diesen Ablauf zu implementieren. Doch mit der Kombination aus HTMX und Django Form lässt sich dies wesentlich einfacher nur mit serverseitigem Code und Templates bewerkstelligen.


Braucht man dann keinen Serializer mehr?

Zu sagen, man bräuchte den Serializer gar nicht, wäre wohl zu weit hergeholt. Ich denke, es wäre besser, es so auszudrücken: Man sollte den Serializer nicht unbedingt als Standard wählen.

Tatsächlich ist der DRF Serializer in folgenden Situationen weiterhin sehr attraktiv:

  • Wenn DRF bereits im gesamten Projekt intensiv genutzt wird.
  • Wenn dieselbe Validierungslogik in APIs und serverseitig gerenderten Ansichten wiederverwendet werden soll.
  • Wenn die Eingabeprüfungslogik komplex ist und bereits gut im Serializer organisiert wurde.
  • Wenn geplant ist, dieselbe Funktionalität später auch für mobile Apps oder externe APIs bereitzustellen.

Das heißt, es geht beim Serializer nicht um die Frage, "Kann er mit HTMX verwendet werden?", sondern eher um die Frage, "Bringt es architektonisch einen Vorteil, den Serializer hier überhaupt einzusetzen?"


Der Serializer kann ebenfalls verwendet werden

Nehmen wir zum Beispiel an, es existiert bereits ein Serializer wie der folgende:

from rest_framework import serializers

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

In diesem Fall kann er auch problemlos in HTMX-Anfragen verwendet werden.

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)

Wie Sie sehen, gibt es technisch keinerlei Probleme. Der Serializer ist schließlich kein Tool, das nur JSON akzeptiert.

Doch hier zeigen sich subtile Unterschiede.

Bei Verwendung von Form:

  • Beibehaltung der Eingabewerte
  • Feldweises Rendering
  • Fehlerbindung
  • Verbindung mit dem Template

Dies alles geschieht nahtlos.

Bei Verwendung des Serializers hingegen:

  • serializer.errors müssen manuell an die Template-Struktur angepasst werden.
  • Bestehende Eingabewerte müssen separat übergeben werden.
  • Der Entwickler muss sich stärker um die Verbindung zum Neu-Rendering des HTML-Formulars kümmern.

Kurz gesagt, es ist nutzbar, erfordert aber etwas mehr manuellen Aufwand.

Genau an diesem Punkt kann der Serializer bei der Verwendung mit HTMX etwas erzwungen wirken.


Fazit

Im letzten Teil haben wir untersucht, wie HTMX Daten sendet. In diesem Teil haben wir nun zusammengefasst, wie diese Daten in Django am natürlichsten validiert werden können.

  • HTMX harmoniert grundsätzlich gut mit Formulardaten.
  • Django Form ist genau das Tool, das für diesen Workflow entwickelt wurde.
  • Betrachtet man daher nur die Kompatibilität mit HTMX, ist Form die natürlichste Wahl.
  • Der DRF Serializer kann verwendet werden, ist aber eher eine strategische Option.

Persönlich denke ich, dass man, um HTMX richtig nutzen zu können, nicht nur ein Gefühl dafür entwickeln muss, "AJAX HTML-ähnlich zu behandeln", sondern auch "Django Form und Form-Tags effektiv einzusetzen."


Weiterführende Artikel