Open-Source Wikis

/

Django

/

Systems

/

Forms

django/django

Forms

django/forms/ is the form library: validation, HTML rendering, and a model-bound dialect (ModelForm) that hooks the ORM in so model definitions become create/update forms with one line of glue.

Purpose

Take an HTTP request body, validate it against a declarative schema, and either return a Python data structure or render the schema with errors back to the user as HTML. The framework supports plain forms, model-bound forms, formsets (collections of forms), and inline formsets (parent + child relationship).

Directory layout

django/forms/
├── __init__.py
├── boundfield.py      # BoundField: a (form, field, name) triple used at render time
├── fields.py          # Field classes: CharField, IntegerField, DateField, EmailField, … (~49 KB)
├── forms.py           # Form, BaseForm; the declarative form class
├── formsets.py        # BaseFormSet, ManagementForm, formset_factory
├── models.py          # ModelForm, BaseModelFormSet, inlineformset_factory (~62 KB)
├── renderers.py       # form template renderers (DjangoTemplates / Jinja2 / DjangoDivFormRenderer)
├── widgets.py         # Widget classes: TextInput, CheckboxInput, Select, … (~41 KB)
├── utils.py           # ErrorDict, ErrorList, pretty_name
├── jinja2/            # Jinja2 templates for form rendering
└── templates/         # Django templates for form rendering

Key abstractions

Type File Role
Form / BaseForm forms.py Declarative form class users subclass
Field fields.py A single form field; validates and coerces
Widget widgets.py The HTML rendering of a field
BoundField boundfield.py A field bound to a form instance for rendering
ErrorList, ErrorDict utils.py Per-field and per-form error containers
BaseFormSet formsets.py A collection of forms
ModelForm models.py A form derived from a model's fields
BaseModelFormSet models.py A formset of model-bound forms
BaseInlineFormSet models.py A formset for child rows of a parent model
Renderer renderers.py Resolves form templates

How it works

graph TD
    Request["request.POST"]
    Form["MyForm(data=request.POST)"]
    Bound["form.is_bound = True"]
    Validate["form.is_valid()<br/>→ form.full_clean()"]
    PerField["field.clean(value):<br/>to_python → validate → run_validators"]
    Form_clean["form.clean()<br/>(cross-field validation)"]
    Cleaned["form.cleaned_data"]
    Errors["form.errors"]

    Request --> Form
    Form --> Bound
    Bound --> Validate
    Validate --> PerField
    PerField --> Form_clean
    Form_clean --> Cleaned
    PerField -->|on error| Errors
    Form_clean -->|on error| Errors

Declarative metaclass

Form is constructed by DeclarativeFieldsMetaclass. The metaclass scans the class body for Field instances and stores them in base_fields. At instance time, each Field becomes a BoundField for rendering.

Field declaration order is preserved (Python 3.7+ dicts are ordered) so rendering matches the source.

Validation pipeline

form.is_valid() calls form.full_clean(), which:

  1. _clean_fields() — for each field, get the raw value from data, call field.clean(value). clean() is a three-step pipeline:
    • to_python(value) — type coercion.
    • validate(value) — basic validity (required, allowed choices).
    • run_validators(value) — additional validators=[] from the field declaration.
  2. _clean_form() — call form.clean() for cross-field rules. Override on subclasses.
  3. _post_clean()ModelForm uses this to run model-level validators (Model.full_clean()).

After full_clean(), form.cleaned_data and form.errors are populated.

Rendering

Calling str(form) (or form.as_p(), form.as_div(), etc.) renders the form via the configured renderer. The renderer resolves a template (django/forms/templates/django/forms/p.html, div.html, etc.) and feeds it the bound form. Each field gets a BoundField that knows how to render its widget, label, and errors.

The default in 5.x+ is as_div, which uses <div> blocks instead of the older <p> or <table> rendering.

Widgets

A Widget is a Python class that knows how to render a field as HTML and how to extract the field's value from request data. Built-in widgets cover the common HTML inputs (TextInput, Textarea, Select, SelectMultiple, CheckboxInput, RadioSelect, FileInput, DateInput, etc.) and a few composites (SplitDateTimeWidget, MultiWidget).

Widgets have their own template files under templates/django/forms/widgets/.

ModelForm

ModelForm (forms/models.py) is the bridge to the ORM. The metaclass walks Meta.model._meta.fields and creates a Field for each one via formfield_callback. The default mapping lives on each model field: CharField.formfield() returns a forms.CharField, etc.

ModelForm.save():

  1. Calls Model.full_clean() (via _post_clean).
  2. Sets the cleaned values on the model instance.
  3. If commit=True, calls Model.save().
  4. Returns the model instance.

For ManyToManyFields, the form has to hold the values until after save(commit=True) because M2M relations require the parent to have a primary key.

Formsets

formset_factory(MyForm, extra=2) creates a BaseFormSet subclass. The formset:

  • Manages a list of bound forms.
  • Renders a hidden ManagementForm with total_form_count, initial_form_count, max_num_form_count, and a hidden prefix.
  • Validates each form individually plus a formset-level clean().

modelformset_factory and inlineformset_factory return formsets that work with ModelForms, including the parent-child relationship for inline formsets.

Integration points

  • ORM (orm) — model fields define formfield() to produce form fields; ModelForm consumes that.
  • Views (views) — FormView, CreateView, UpdateView, DeleteView integrate the validation/render cycle.
  • Templates (templates) — form rendering is template-driven; both DTL and Jinja2 templates live under forms/templates/ and forms/jinja2/.
  • Admin (contrib/admin/) — the admin's change/add forms are heavily customised ModelForms.
  • Validators (django/core/validators.py) — most form fields delegate to validators.
  • i18n — error messages use gettext_lazy.

Entry points for modification

  • Custom field: subclass Field in forms/fields.py. Override to_python, validate, run_validators. Pair with a Widget if needed.
  • Custom widget: subclass Widget in forms/widgets.py. Override format_value, get_context, value_from_datadict. Add a template file for rendering.
  • Custom renderer: subclass BaseRenderer in forms/renderers.py. Set FORM_RENDERER in settings.
  • ModelForm field defaults: change formfield_callback in your ModelForm.Meta, or add formfield() to a custom model field.

Key source files

File Purpose
django/forms/forms.py Form, BaseForm, DeclarativeFieldsMetaclass
django/forms/fields.py Field classes (~49 KB)
django/forms/widgets.py Widget classes (~41 KB)
django/forms/models.py ModelForm, formset factories, BaseInlineFormSet (~62 KB)
django/forms/formsets.py BaseFormSet, ManagementForm, formset_factory
django/forms/boundfield.py BoundField
django/forms/renderers.py Form template renderers
django/forms/utils.py ErrorList, ErrorDict, pretty_name

Where to read tests

  • tests/forms_tests/ — exhaustive form, field, widget coverage.
  • tests/model_forms/ModelForm integration with the ORM.
  • tests/forms_tests/tests/test_formsets.py, test_inlineformsets.py — formset behaviour.
  • tests/admin_views/ — the admin exercises forms end-to-end.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Forms – Django wiki | Factory