Django / Request and data foundations / Lesson 5
Forms, validation, and CSRF
Separate bound input, cleaned values, error feedback, and request-origin protection while preserving accessible native form behavior.
On this page
Lesson goals
- A form is an input contract
- Validate a complete local form fixture
- Keep validation and request protection distinct
A form is an input contract
A Django Form declares fields and validation rules for a particular operation. An unbound form describes an initial interface, while a bound form contains submitted data and can report validation results. A bound form with an empty mapping is not equivalent to an unbound form. Validate before reading cleaned_data, and use cleaned values rather than returning to raw request text afterward. Field validation can normalize types, such as turning accepted numeric text into an integer, but the application still needs domain rules, authorization, and database constraints. A valid form does not reserve stock or prove a concurrent write will succeed.
Make error correction part of the workflow
A useful form keeps entered safe values, associates field errors and help text, and tells the person how to recover. Requiredness, maximum lengths, and numeric bounds should match the operation rather than merely the database’s broad storage capacity. Cross-field checks belong in appropriate clean methods, with field or non-field errors chosen deliberately. Do not use placeholder text as the only label or color as the only error meaning. Render the form’s accessible structure or customize it carefully, and test the result with errors present. Browser-side constraints improve interaction but cannot replace server validation of the submitted request.
Validate a complete local form fixture
The first example defines a bounded seat-request form and binds two synthetic submissions. A valid quantity becomes a Python integer in cleaned_data, while zero fails the declared minimum. The code only validates data in memory and does not create a booking or contact a database. Printed validity and cleaned values are separate observations. A real booking workflow must apply authorization, current capacity checks, and transactional write rules after validation. Keeping this boundary narrow makes it possible to test malformed, missing, and out-of-range input without needing a web server or live account.
from django.conf import settings
settings.configure(
SECRET_KEY="synthetic-course-key",
USE_I18N=False, USE_TZ=True,
INSTALLED_APPS=[],
DATABASES={"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}},
DEFAULT_AUTO_FIELD="django.db.models.AutoField",
ALLOWED_HOSTS=["testserver"],
)
import django
django.setup()
from django import forms
class SeatRequestForm(forms.Form):
name = forms.CharField(max_length=60)
quantity = forms.IntegerField(min_value=1, max_value=5)
good = SeatRequestForm({"name": "Mira", "quantity": "3"})
bad = SeatRequestForm({"name": "Mira", "quantity": "0"})
print(good.is_valid(), good.cleaned_data["quantity"], type(good.cleaned_data["quantity"]).__name__)
print(bad.is_valid(), "quantity" in bad.errors)
True 3 int
False True
Validation accepts three and cleans it to int, while zero receives a quantity error. No reservation or database write occurs.
Protect browser writes with the CSRF mechanism
The second fragment is a template for a normal internal POST form rendered through the configured Django template backend with the request context and CSRF middleware. The csrf_token tag supplies the hidden token under that arrangement. It is not an authentication token, and it does not prove permission for the submitted operation. Never put an internal CSRF token into a form targeting an unrelated external site. AJAX clients need the documented token and origin handling too. If a 403 occurs, diagnose cookie delivery, token, origin, and proxy assumptions instead of making the view csrf_exempt as a general repair.
<form method="post" action="{% url 'seat-request' %}">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Request seats</button>
</form>
In its declared app context: labeled form controls and a hidden CSRF token for the internal POST.
Requires the named internal route, a bound or unbound form, request-aware template rendering, and the normal CSRF middleware. It is not a standalone document or executed request.
Keep validation and request protection distinct
CSRF checks assess aspects of an unsafe browser request under Django’s mechanism, not whether its title or quantity is valid. Authentication supplies identity, authorization decides allowed operations, and form validation checks the input contract. Test these independently so a valid-field test does not hide a missing access boundary. Safe methods should remain free of requested state changes. After a successful browser write, a suitable redirect can reduce accidental resubmission, but it does not create server idempotency by itself. Report success only after the owned operation actually succeeds, and preserve a recoverable response when validation or persistence fails.
Common errors and fixes
Raw values are used after successful validation
- Why it happens
- The action ignores cleaned_data.
- How to fix it
- Consume the form’s approved cleaned values.
- Prevent it next time
- Test type and normalization expectations.
A CSRF failure is “fixed” by disabling protection
- Why it happens
- Origin and token evidence was never investigated.
- How to fix it
- Restore protection and diagnose the actual request path.
- Prevent it next time
- Keep a protected-request regression case.
A valid form is treated as a completed booking
- Why it happens
- Input acceptance was confused with persistence and capacity.
- How to fix it
- Perform authorized domain checks and writes afterward.
- Prevent it next time
- Test validation separately from business outcomes.
Check your understanding
1. Why must the action use cleaned_data instead of request.POST after is_valid?
Show worked solution
The form has established accepted fields, conversions, and validation results. Returning to raw input discards that boundary and may reintroduce extra fields or wrong types. Construct the domain command from the approved cleaned values, while keeping server-owned identity fields outside the client’s control.
2. Does a valid CSRF token permit changing another user’s booking?
Show worked solution
No. CSRF protection and resource authorization answer different questions. The view must still authenticate the caller, scope the booking lookup, authorize the operation, validate input, and preserve the domain invariant. A token cannot substitute for any of those decisions.
Key takeaways
- Bound forms validate submitted data.
- Cleaned values and raw input are different boundaries.
- CSRF does not replace authentication or authorization.