djust docs
Browse documentation

Forms

On this page

djust handles forms over WebSocket — no page reloads, no JavaScript, no API layer.

The full forms guide is at guides/forms.md.

Quick Reference

Simple Form (no validation)

from djust import LiveView
from djust.decorators import event_handler

class TodoView(LiveView):
    template_name = "todos.html"

    def mount(self, request, **kwargs):
        self.items = []

    @event_handler()
    def add_item(self, title="", **kwargs):
        if title.strip():
            self.items.append(title.strip())
<form dj-submit="add_item">
    {% csrf_token %}
    <input type="text" name="title" placeholder="New item" />
    <button type="submit">Add</button>
</form>

With Django Forms Validation

from django import forms
from djust import LiveView
from djust.forms import FormMixin

class ContactForm(forms.Form):
    name = forms.CharField(max_length=100)
    email = forms.EmailField()
    message = forms.CharField(widget=forms.Textarea)

class ContactView(FormMixin, LiveView):
    template_name = "contact.html"
    form_class = ContactForm

    def form_valid(self, form):
        send_email(form.cleaned_data)
        self.success_message = "Message sent!"

    def form_invalid(self, form):
        self.error_message = "Please fix the errors below."
<form dj-submit="submit_form">
    {% csrf_token %}
    <input name="name" value="{{ form_data.name }}" dj-change="validate_field" />
    {% if field_errors.name %}<span>{{ field_errors.name.0 }}</span>{% endif %}

    <input name="email" type="email" value="{{ form_data.email }}" dj-change="validate_field" />
    {% if field_errors.email %}<span>{{ field_errors.email.0 }}</span>{% endif %}

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

dj-change="validate_field" validates each field on blur — instant inline errors without a full submission.

Editing Existing Records (ModelForm)

class ArticleEditView(FormMixin, LiveView):
    template_name = "article_form.html"
    form_class = ArticleForm

    def mount(self, request, pk=None, **kwargs):
        if pk:
            self._model_instance = Article.objects.get(pk=pk)
        super().mount(request, **kwargs)

    def form_valid(self, form):
        form.save()
        self.success_message = "Saved!"

FormMixin Template Variables

VariableTypeDescription
form_datadictCurrent field values
field_errorsdictPer-field errors {field: [errors]}
form_errorslistNon-field errors from clean() ({} until an invalid submit)
is_validboolResult of last submission
success_messagestrSuccess message your view sets (empty by default)
error_messagestrError message your view sets (empty by default)
form_choicesdictChoices for choice fields, keyed by field name

The Django form instance is available in Python as self.form_instance; it is not in the template context.

Multi-Step Wizards

For guided multi-step forms, use WizardMixin:

from djust import WizardMixin, LiveView

class SignupWizard(WizardMixin, LiveView):
    wizard_steps = [
        {"name": "personal", "title": "Personal Info", "form_class": PersonalInfoForm},
        {"name": "address",  "title": "Address",       "form_class": AddressForm},
        {"name": "review",   "title": "Review"},
    ]

    def on_wizard_complete(self, step_data):
        User.objects.create(**step_data["personal"])

See guides/wizards.md for the full wizard guide.

Full Guide

See guides/forms.md for:

  • Real-time validation details
  • form.as_live auto-rendering
  • Confirmation dialogs (dj-confirm)
  • dj-model vs dj-submit comparison
  • Draft mode auto-save (see State Management)

Markdown editor

See the Markdown Editor guide for optional Visual/Markdown editing, native form integration, asset loading, theme variables and editing limitations.