Typorithm · easy

Forms and cleaned data

pythondjangoindependent-practice
Saved on this device
Full lesson · 4 learning steps Open any exercise for focused practice
1

Step 1 of 4 · intro card

Forms and cleaned data

0%

Concept and contract

Use the real form validation boundary before consuming submitted values or attempting persistence.

Forms separate submitted values, validation errors and accepted application values. HTML submissions commonly arrive as text, even when a field represents an integer.

This lesson keeps those representations separate instead of using the raw input dictionary after a superficial check. CandidateForm declares required fields and their restrictions, while the helper exposes either field error names or cleaned values. Nothing is saved to a database here.

Mechanism and worked example

is_valid runs field conversion and validation, after which cleaned_data contains the accepted types.

is_valid runs field conversion and validation, after which cleaned_data contains the accepted types. CharField strips surrounding whitespace under its default policy, while IntegerField converts suitable textual input and enforces the minimum.

The returned years value is therefore an integer rather than the original string. Error output uses stable field names; comparing full translated error messages would unnecessarily tie the fixture to presentation and localization details.

Complete worked example

python

from bootstrap_lab import django_setup
django_setup(__name__)

from django import forms

class CandidateForm(forms.Form):
    title = forms.CharField(max_length=20)
    years = forms.IntegerField(min_value=1)

def validate_form(data):
    form = CandidateForm(data)
    if not form.is_valid():
        return {"valid": False, "errors": sorted(form.errors)}
    return {"valid": True, **form.cleaned_data}
Reasoning
is_valid runs field conversion and validation, after which cleaned_data contains the accepted types. CharField strips surrounding whitespace under its default policy, while IntegerField converts suitable textual input and enforces the minimum.

Counterexamples and common mistakes

The counterexample permits zero years despite the declared minimum of one.

The counterexample permits zero years despite the declared minimum of one. A different mistake is checking is_valid and then returning the original uncleaned dictionary.

That loses conversion and may preserve rejected or irrelevant values. A Django form is not automatically a strict JSON schema, and extra submitted keys do not become authorized model fields. Only explicitly selected cleaned values should reach a later write operation.

Counterexample: a runnable framework mistake

python

from bootstrap_lab import django_setup
django_setup(__name__)

from django import forms

class CandidateForm(forms.Form):
    title = forms.CharField(max_length=20)
    years = forms.IntegerField(min_value=0)

def validate_form(data):
    form = CandidateForm(data)
    if not form.is_valid():
        return {"valid": False, "errors": sorted(form.errors)}
    return {"valid": True, **form.cleaned_data}
Reasoning
The counterexample permits zero years despite the declared minimum of one. A different mistake is checking is_valid and then returning the original uncleaned dictionary.

Independent practice: one bounded framework change

Add a maximum of twenty years while retaining the required title and minimum.

Add a maximum of twenty years while retaining the required title and minimum. Begin with a valid boundary value of twenty, then reject twenty-one and keep rejecting zero.

The task changes one field constraint rather than introducing a custom validation framework. Preserve the cleaned integer and stripped title in accepted output, and keep invalid output limited to its existing error field names.

Step 1: Add an upper bound of twenty years. Step 2: Keep cleaned values distinct from raw submitted strings.

Practice starter: solve before reading the solution

python

from bootstrap_lab import django_setup
django_setup(__name__)

from django import forms

class CandidateForm(forms.Form):
    title = forms.CharField(max_length=20)
    years = forms.IntegerField(min_value=1)

def validate_form(data):
    # Remove this placeholder, then adapt the retained baseline:
    # Add an upper bound of twenty years.
    raise NotImplementedError
    form = CandidateForm(data)
    if not form.is_valid():
        return {"valid": False, "errors": sorted(form.errors)}
    return {"valid": True, **form.cleaned_data}
Reasoning
Add an upper bound of twenty years. Keep cleaned values distinct from raw submitted strings.
  • Hint 1: Change the IntegerField constraint rather than post-processing invalid data.
  • Hint 2: Test both twenty and twenty-one.

Solution, tests and trade-offs

The cases exercise valid textual numbers, surrounding title whitespace, an empty required title and the invalid minimum.

The cases exercise valid textual numbers, surrounding title whitespace, an empty required title and the invalid minimum. Practice adds both sides of the upper boundary.

Because the checks invoke the actual form class, they can catch a field definition that differs from a separate hand-written expectation. Form validity still does not prove that a requested record exists, that its owner is correct or that a database constraint can never fail.

Practice solution and acceptance checks

python

from bootstrap_lab import django_setup
django_setup(__name__)

from django import forms

class CandidateForm(forms.Form):
    title = forms.CharField(max_length=20)
    years = forms.IntegerField(min_value=1, max_value=20)

def validate_form(data):
    form = CandidateForm(data)
    if not form.is_valid():
        return {"valid": False, "errors": sorted(form.errors)}
    return {"valid": True, **form.cleaned_data}
Reasoning
The cases exercise valid textual numbers, surrounding title whitespace, an empty required title and the invalid minimum. Practice adds both sides of the upper boundary.

Interview reasoning and next step

Explain the sequence from raw submission through conversion, validation and cleaned_data.

Explain the sequence from raw submission through conversion, validation and cleaned_data. A cross-field clean method can express relationships between accepted values, while a database constraint can protect invariant state across multiple writers.

ModelForm can connect selected fields to a model, but careless field selection can expose privileged attributes. The next lesson demonstrates why model metadata and database-enforced constraints deserve their own explicit boundary.

Optional stretch — after the core task works: Add a cross-field rule and attach its error to the appropriate form boundary.

Computer practice: https://applydjinn.com/typorithm/python-frameworks-lab-20261105.zip. Install the pinned dependencies in an isolated environment, run verify.py --track django, then edit the retained exercise baseline and check it with --practice dj-forms. The lab uses owned in-memory databases, local framework clients and tiny CPU tensors. It never reads the working ApplyDjinn database, contacts production feeds or downloads models.

Use the real form validation boundary before consuming submitted values or attempting persistence.