Forms
Integrate Django Forms with real-time validation and seamless submission.
Quick Start
1. Create a Django Form
forms.py
1# forms.py
2from django import forms
3
4class ContactForm(forms.Form):
5 name = forms.CharField(max_length=100)
6 email = forms.EmailField()
7 message = forms.CharField(widget=forms.Textarea)
2. Create a LiveView with FormMixin
views.py
1# views.py
2from djust import LiveView
3from djust.forms import FormMixin
4from .forms import ContactForm
5
6class ContactView(FormMixin, LiveView):
7 template_name = 'contact.html'
8 form_class = ContactForm
9
10 def form_valid(self, form):
11 send_email(to='support@example.com', body=form.cleaned_data['message'])
12 self.success_message = 'Message sent!'
13
14 def form_invalid(self, form):
15 pass # Errors shown automatically
3. Create the Template
templates/contact.html
1{% load live_tags %}
2<div dj-liveview>
3 {% if success_message %}
4 <div class="alert alert-success">{{ success_message }}</div>
5 {% else %}
6 <form dj-submit="submit">
7 {% csrf_token %}
8
9 {% live_field view "name" %}
10 {% live_field view "email" %}
11 {% live_field view "message" %}
12
13 <button type="submit">Send</button>
14 </form>
15 {% endif %}
16</div>
Real-Time Validation
On Blur (dj-change)
Validate when field loses focus:
<input name="email" dj-change="email" value="{{ form.email.value }}">
On Keystroke (dj-input with debounce)
from djust import debounce
class SignupView(FormMixin, LiveView):
@debounce(wait=0.3)
def username(self, value: str = "", **kwargs):
self.form_data['username'] = value
self.validate_field('username', value)
# Custom async validation
if User.objects.filter(username=value).exists():
self.field_errors['username'] = 'Username already taken'
Custom Validation
def validate_field(self, field_name, value, **kwargs):
super().validate_field(field_name, value, **kwargs)
if field_name == 'email':
if not value.endswith('@company.com'):
self.field_errors['email'] = 'Must use company email'
ModelForms
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
fields = ['title', 'content']
class ArticleCreateView(FormMixin, LiveView):
form_class = ArticleForm
def form_valid(self, form):
article = form.save(commit=False)
article.author = self.request.user
article.save()
self.success = True
Multi-Step Forms
class WizardView(LiveView):
def mount(self, request, **kwargs):
self.step = 1
self.data = {}
def next_step(self, **kwargs):
if self.validate_current_step():
self.step += 1
def prev_step(self):
if self.step > 1:
self.step -= 1
def submit(self, **kwargs):
if self.step == 3 and self.validate_current_step():
self.create_order()
self.success = True
File Uploads
class UploadView(LiveView):
def handle_upload(self, file=None, **kwargs):
if file:
if file.size > 10 * 1024 * 1024: # 10MB
self.error = 'File too large'
return
from django.core.files.storage import default_storage
path = default_storage.save(f'uploads/{file.name}', file)
self.uploaded_file = path
Form Styling
Bootstrap 5
<div class="mb-3">
<label class="form-label">Email</label>
<input type="email" name="email"
class="form-control {% if field_errors.email %}is-invalid{% endif %}"
dj-change="email" value="{{ form.email.value|default:'' }}">
{% if field_errors.email %}
<div class="invalid-feedback">{{ field_errors.email }}</div>
{% endif %}
</div>
Tailwind
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700">Email</label>
<input type="email" name="email"
class="mt-1 block w-full rounded-md border-gray-300
{% if field_errors.email %}border-red-300{% endif %}"
dj-change="email" value="{{ form.email.value|default:'' }}">
{% if field_errors.email %}
<p class="mt-2 text-sm text-red-600">{{ field_errors.email }}</p>
{% endif %}
</div>
Best Practices
- Always include
- Use
@debouncefor real-time validation - Disable submit button during processing
- Clear form after successful submission
- Handle errors gracefully
Next Steps
- Testing - Test form validation
- API Reference - FormMixin API