Django Forms (django.forms.Form) provide a declarative way to define validation logic, rendering, and data cleaning for user input, while ModelForms (django.forms.ModelForm) automatically generate form fields from a model’s field definitions, handling model instance creation and updates through form.save().
Forms solve the problem of safely handling user input — validating, sanitizing, and converting raw HTTP POST data into structured Python objects. A Form declares fields with validators and widgets; on submission, form.is_valid() runs field cleaning (clean_<field>()), form-level cleaning (clean()), and returns cleaned data. ModelForms extend this by mapping form fields to model fields, enabling form.save() to create or update model instances atomically.
- Form defined — Class with field instances (
CharField,EmailField,ModelChoiceField, etc.) - GET request — View instantiates empty form; template renders
{{ form.as_p }}or manual{{ field }} - POST request — View instantiates
Form(request.POST)(andrequest.FILESfor uploads) - Validation runs —
is_valid()callsfull_clean()→ fieldclean()→clean_<field>()→clean() - Cleaned data —
form.cleaned_datadict available if valid - Save/Model save —
form.save()creates/updates model (ModelForm) or custom logic (Form)
- Field types:
CharField,IntegerField,EmailField,ChoiceField,ModelChoiceField,ModelMultipleChoiceField,FileField,ImageField,DateTimeField,BooleanField - Widgets control rendering:
TextInput,Textarea,Select,CheckboxSelectMultiple,HiddenInput,DateTimeInput— customize HTML attributes - Validation layers: Field
clean()(type/format) →clean_<field>()(field-specific) →clean()(cross-field) →cleaned_data - ModelForm Meta:
model,fields/exclude,widgets,labels,help_texts,error_messages,field_classes save(commit=False): Returns unsaved instance for pre-save modification (e.g., setauthor=request.user)
- Built from: ORM — ModelForm introspects model fields
- Built from: Form Fields — Field definitions and validation
- Built from: Widgets — HTML rendering customization
- Built from: Validators — Reusable validation logic
- Builds into: Form Validation — Multi-layer cleaning pipeline
- Builds into: ModelForm Save Logic —
save(),save(commit=False) - Builds into: Form Rendering —
as_p,as_table,as_ul, manual{{ field }} - Builds into: Formsets — Multiple forms on one page
- Builds into: File Uploads —
FileField,ImageField,request.FILES - Contrasts with: WTForms — Flask’s form library, similar but separate
- Contrasts with: Pydantic — FastAPI’s validation, type-hint based, no HTML rendering
- Related: CSRF Protection —
{% csrf_token %}required for POST forms - Related: Form Template Tags —
{{ form.errors }},{{ field.label_tag }}
clean()vsclean_<field>():clean()runs after all field cleaning;cleaned_datamay be incomplete if field errors exist- ModelForm
excludevsfields:fields = '__all__'includes future model fields (security risk); explicitfieldspreferred save(commit=False): Must callinstance.save()manually; M2M needsform.save_m2m()after- File uploads: Need
enctype="multipart/form-data"on<form>;request.FILESseparate fromrequest.POST - Formset management form: Hidden
TOTAL_FORMS,INITIAL_FORMSrequired;can_delete=TrueaddsDELETEcheckbox