Quick Start¶
Basic form¶
With FORM_RENDERER set, any Django form renders with DaisyUI styling:
# forms.py
from django import forms
class ContactForm(forms.Form):
name = forms.CharField()
email = forms.EmailField()
message = forms.CharField(widget=forms.Textarea)
<!-- template.html -->
{% load formwork %}
{% formwork_css %}
<form method="post" class="max-w-md mx-auto">
{% csrf_token %}
{{ form }}
<button type="submit" class="btn btn-primary mt-4">Send</button>
</form>
{% formwork_js %}
Each field renders inside a <fieldset class="fieldset"> with a label, help text, and an error tooltip.
Per-form styling¶
If you don't want to set FORM_RENDERER globally, use the form base classes:
from django_formwork.forms import FormworkForm, FormworkModelForm
class ContactForm(FormworkForm):
name = forms.CharField()
email = forms.EmailField()
class ProfileForm(FormworkModelForm):
class Meta:
model = Profile
fields = ["name", "bio", "avatar"]
Custom widgets¶
from django_formwork.widgets import (
Toggle, Range, Rating, PasswordReveal,
SearchSelect, MultiSelect, ComboBox, DataList,
FileDropZone, ImageDropZone, ValidatedTextarea,
)
class ExampleForm(forms.Form):
# Toggle switch instead of checkbox
dark_mode = forms.BooleanField(widget=Toggle)
# Range slider
volume = forms.IntegerField(widget=Range(attrs={"min": 0, "max": 100}))
# Star rating
rating = forms.TypedChoiceField(
choices=Rating.make_choices(5), coerce=int, widget=Rating,
)
# Password with show/hide toggle
password = forms.CharField(widget=PasswordReveal)
# Searchable single-select dropdown
country = forms.ChoiceField(
choices=[("us", "United States"), ("de", "Germany")],
widget=SearchSelect,
)
# Multi-select with checkboxes
languages = forms.MultipleChoiceField(
choices=[("py", "Python"), ("js", "JavaScript")],
widget=MultiSelect,
)
# Text input with autocomplete suggestions
tags = forms.CharField(
widget=ComboBox(suggestions=["Python", "Django", "htmx"]),
)
# Native browser datalist
browser = forms.CharField(
widget=DataList(datalist=["Chrome", "Firefox", "Safari"]),
)
# Drag-and-drop file upload
attachment = forms.FileField(widget=FileDropZone)
# Image upload with preview
avatar = forms.ImageField(widget=ImageDropZone)
Server-side textarea validation¶
ValidatedTextarea sends the textarea content to a server-side view as the user types, and highlights error spans in real time.
# views.py
from django_formwork.views import FormworkValidateView
class SpellCheckView(FormworkValidateView):
def get_errors(self, text: str, **kwargs) -> list[dict]:
errors = []
for match in find_misspellings(text):
errors.append({
"message": f"Misspelled: {match.word}",
"start": match.start,
"end": match.end,
})
return errors
# forms.py
content = forms.CharField(
widget=ValidatedTextarea(validate_url=reverse_lazy("spell-check")),
)
CSRF exemption
FormworkValidateView is CSRF-exempt because it performs read-only validation. Do not use it for operations with side effects (database writes, emails, etc.). For those, use a regular Django view with CSRF protection.
Server-side search¶
SearchSelect, MultiSelect, and ComboBox register a search endpoint automatically when used on a FormworkForm. Wire the URL pattern once:
# urls.py
from django.urls import include, path
urlpatterns = [
path("__formwork__/", include("django_formwork.urls")),
]
There are two registration paths. For model-backed search, pair the widget with search_fields against a ModelChoiceField queryset:
from django.contrib.auth.decorators import login_required
from django_formwork.forms import FormworkForm
from django_formwork.widgets import SearchSelect
class CityForm(FormworkForm):
city = forms.ModelChoiceField(
queryset=City.objects.all(),
widget=SearchSelect(
search_fields=["name", "country__name"],
search_decorator=login_required,
),
)
For choices-backed search, define a search_choices_<fieldname> method on the form:
class TagForm(FormworkForm):
tags = forms.ChoiceField(widget=SearchSelect)
def search_choices_tags(self, query, request):
return [
{"value": t.slug, "label": t.name}
for t in Tag.objects.filter(name__icontains=query)[:20]
]
search_decorator is required whenever auto-registration is active. It protects the generated endpoint; pass None explicitly for a public endpoint. The views reference covers the decorator and the FormworkSearchView base class for hand-written endpoints.