CodingNeed.

2 · Data and boundaries · 30 MIN

Django forms, CSRF and server validation

A form validates untrusted fields before domain work begins.

Django forms turn raw input into cleaned_data only after validation succeeds. Bound the length and range in the form rather than trusting HTML attributes. A rendered POST form should include csrf_token and keep CsrfViewMiddleware enabled. CSRF protection does not replace authentication or record ownership checks. This example previews cleaned values and deliberately performs no database write, making validation and method behavior easy to test before introducing persistence.

Keep valid fields visible after another field fails.

Read the example

# courses/forms.py
from django import forms
class PlanForm(forms.Form):
    title = forms.CharField(min_length=3, max_length=80)
    weekly_goal = forms.IntegerField(min_value=1, max_value=20)
# courses/views.py
from django.shortcuts import render
from .forms import PlanForm
def plan(request):
    form = PlanForm(request.POST if request.method == "POST" else None)
    preview = form.cleaned_data if request.method == "POST" and form.is_valid() else None
    return render(request, "courses/plan.html", {"form": form, "preview": preview})
# courses/templates/courses/plan.html
# <form method="post">{% csrf_token %}{{ form.as_p }}<button>Preview</button></form>
# {% if preview %}<p>{{ preview.title }}: {{ preview.weekly_goal }}</p>{% endif %}
Check the expected output
The form shows field errors for invalid input and a preview for a valid title and goal; a CSRF-less POST is rejected when middleware checks apply.

Your challenge

Register the form route, retain CSRF middleware and test valid, invalid and missing-token submissions before adding authenticated persistence.

Solution cost: O(f + m) field and string validation. time · O(f + m) bound form data. space

Common trap

Django’s default test client relaxes CSRF checks unless explicitly enabled.

Study the project implementation
# courses/forms.py
from django import forms
class PlanForm(forms.Form):
    title = forms.CharField(min_length=3, max_length=80)
    weekly_goal = forms.IntegerField(min_value=1, max_value=20)
# courses/views.py
from django.shortcuts import render
from .forms import PlanForm
def plan(request):
    form = PlanForm(request.POST if request.method == "POST" else None)
    preview = form.cleaned_data if request.method == "POST" and form.is_valid() else None
    return render(request, "courses/plan.html", {"form": form, "preview": preview})
# courses/templates/courses/plan.html
# <form method="post">{% csrf_token %}{{ form.as_p }}<button>Preview</button></form>
# {% if preview %}<p>{{ preview.title }}: {{ preview.weekly_goal }}</p>{% endif %}

Further reading: Official documentation

Next lesson: FastAPI request and response models

Essential cookies keep your account signed in. Optional analytics is not configured on this site. Your choice does not affect access to lessons.

Read the Privacy Policy. You can change this choice in the footer.