Forms & Validation
📝 Forms & Validation
Form and ModelForm classes do both jobs at once — rendering the fields and validating what comes back — and, without any extra package, protect every form against CSRF the way an entire dedicated course covered doing by hand.
A Basic Form
A Form class describes fields the same way a model does — but these fields describe input validation, not database columns:
is_valid()
Runs every field's validation — EmailField rejects a malformed address, CharField(max_length=100) rejects anything longer — and returns True/False.
form.cleaned_data
Only populated after a successful is_valid() — already type-converted (an IntegerField gives back a real int, not a string).
🧬 ModelForm: Generated From a Model
Writing a Form by hand that mirrors a model's fields is duplicated work — ModelForm generates the form directly from the model itself:
form.save() both validates and persists the model instance — one line replaces manually reading each field off request.POST and constructing a Book(...) by hand.
🛡️ Built-in CSRF Protection
The entire CSRF course was dedicated to the synchronizer token pattern — generate a token, embed it in the form, validate it on submit. Django implements exactly that pattern, on by default, for every POST form:
Django's Built-in CSRF vs Express's Manual Setup
Express: Manual Setup Required
Django: On By Default
CsrfViewMiddleware is already in settings.py's MIDDLEWARE list from the moment startproject creates it — every POST form just needs the one template tag, and Django rejects any POST request missing a valid token with a 403, automatically.
Custom Validation
Beyond a field's built-in checks, a form can define its own validation logic — per-field with clean_<fieldname>(), or across multiple fields with clean():
Mass Assignment: Meta.fields as an Allowlist
Rails' Forms chapter warned about mass assignment vulnerabilities — Django's protection is ModelForm.Meta.fields acting as an explicit allowlist:
Dangerous vs Safe Meta.fields
Dangerous: Exposes Everything
Safe: Explicit Allowlist
💻 Coding Challenges
Challenge 1: Write a ModelForm
Write a AuthorForm(forms.ModelForm) for the Author model from Chapter 5, exposing only name and bio (not birth_year), then write the view function that renders it on GET and saves it on a valid POST.
Goal: Practice ModelForm's Meta.fields allowlist and the is_valid()/save() pattern.
Challenge 2: Add Custom Validation
Add a clean_bio() method to the AuthorForm from Challenge 1 that rejects a bio longer than 500 characters with a clear ValidationError message.
Goal: Practice field-level custom validation beyond what the field type checks automatically.
Challenge 3: Spot the Mass Assignment Risk
Given a ModelForm for a UserProfile model (with fields bio, avatar_url, and is_verified) that uses fields = "__all__", rewrite it to a safe explicit allowlist and explain in one sentence what real-world exploit the original version was vulnerable to.
Goal: Practice recognizing and fixing the Django equivalent of the mass assignment vulnerability the Rails course covered.
{% csrf_token %}Omit {% csrf_token %} from a POST form's template and every single submission fails with 403 Forbidden: CSRF verification failed — not a silent bug, but a confusing one the first time it happens, since GET requests through the same view work fine. Because CsrfViewMiddleware is on by default project-wide, this isn't optional configuration to remember per-view the way Express's csurf setup is — it's a template detail that's easy to skip when copy-pasting a form from example code that used {{ form.as_p }} alone.
🎯 What's Next
The final chapter of this course covers the feature that's arguably Django's biggest single differentiator from FastAPI and Express: Admin Interface & Settings — the auto-generated admin site, and how settings.py ties every piece covered so far together.