In any web application, handling user input securely and effectively is crucial. Django simplifies this process using its forms framework, which provides a powerful way to validate and process form data.
This article explains how to use Django forms with:
-
Built-in and custom validations
-
Template rendering
-
Form submission and processing
-
Model-based forms
-
Tips and common pitfalls
What Are Django Forms?
Django provides a Form
class that allows you to:
-
Define form fields and widgets
-
Handle form validation (cleaning data)
-
Display form errors
-
Bind data from POST requests
-
Easily integrate with templates
Types of Forms in Django
Type | Description |
---|---|
forms.Form |
Basic form, not tied to a model |
forms.ModelForm |
Automatically creates fields from a model |
Step 1: Create a Basic Form
Create a forms.py
file in your app directory.
# forms.py
from django import forms
class ContactForm(forms.Form):
name = forms.CharField(max_length=100)
email = forms.EmailField()
message = forms.CharField(widget=forms.Textarea)
Step 2: Create a View to Handle the Form
Use the form in a view to render and process it.
# views.py
from django.shortcuts import render
from .forms import ContactForm
def contact_view(request):
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
# Process the data
name = form.cleaned_data['name']
email = form.cleaned_data['email']
message = form.cleaned_data['message']
# You could send an email or save the data
return render(request, 'thank_you.html', {'name': name})
else:
form = ContactForm()
return render(request, 'contact.html', {'form': form})
Step 3: Create the Template
<!-- contact.html -->
<h2>Contact Us</h2>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Send</button>
</form>
✅ Step 4: Add URL Pattern
# urls.py
from django.urls import path
from .views import contact_view
urlpatterns = [
path('contact/', contact_view, name='contact'),
]
Form Field Types
Django provides many field types out of the box:
Field Type | Widget |
---|---|
CharField |
TextInput |
EmailField |
EmailInput |
BooleanField |
CheckboxInput |
ChoiceField |
Select |
DateField |
DateInput |
FileField |
FileInput |
MultipleChoiceField |
CheckboxSelectMultiple |
You can customize widgets like so:
name = forms.CharField(widget=forms.TextInput(attrs={'class': 'form-control'}))
Cleaning and Validating Data
Single Field Validation
def clean_email(self):
email = self.cleaned_data['email']
if not email.endswith('@example.com'):
raise forms.ValidationError('Email must be from example.com')
return email
Cross-Field Validation
def clean(self):
cleaned_data = super().clean()
name = cleaned_data.get('name')
message = cleaned_data.get('message')
if name and message and name in message:
raise forms.ValidationError("Don't include your name in the message.")
Using ModelForm
If your form is tied to a model, use ModelForm
to reduce duplication.
Define a Model
# models.py
from django.db import models
class Feedback(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField()
comment = models.TextField()
Create a ModelForm
# forms.py
from django.forms import ModelForm
from .models import Feedback
class FeedbackForm(ModelForm):
class Meta:
model = Feedback
fields = ['name', 'email', 'comment']
Use it in a View
# views.py
def feedback_view(request):
if request.method == 'POST':
form = FeedbackForm(request.POST)
if form.is_valid():
form.save()
return render(request, 'thank_you.html')
else:
form = FeedbackForm()
return render(request, 'feedback.html', {'form': form})
Handling File Uploads
# forms.py
class UploadForm(forms.Form):
file = forms.FileField()
View
def upload_view(request):
if request.method == 'POST':
form = UploadForm(request.POST, request.FILES)
if form.is_valid():
uploaded_file = form.cleaned_data['file']
with open(f'uploads/{uploaded_file.name}', 'wb+') as destination:
for chunk in uploaded_file.chunks():
destination.write(chunk)
else:
form = UploadForm()
return render(request, 'upload.html', {'form': form})
Don't forget to set
MEDIA_ROOT
andMEDIA_URL
insettings.py
and updateurls.py
to serve media files in development.
Tips and Best Practices
Tip | Description |
---|---|
✅ Use {{ form.as_p }} or custom HTML for styling |
|
✅ Always include {% csrf_token %} in forms |
|
✅ Use ModelForm when dealing with models |
|
✅ Use form.is_valid() before accessing cleaned_data |
|
✅ Use widgets for customizing input fields | |
✅ Use required=False if a field is optional |
|
✅ Handle both GET and POST in the view logic |
Common Pitfalls
Problem | Cause | Fix |
---|---|---|
Form not rendering | Forgot to pass it to template | return render(request, 'template.html', {'form': form}) |
Form always invalid | Missed request.POST or wrong field types |
Check form = YourForm(request.POST) and field definitions |
File uploads not working | Forgot to use request.FILES |
Always pass request.FILES when handling file uploads |
CSRF errors | Forgot {% csrf_token %} |
Add it inside the <form> block |
No errors showing | Forgot to display {{ form.errors }} |
Use {{ form.errors }} for debugging |
✅ Summary
Step | Description |
---|---|
1. Create Form | Inherit from forms.Form or forms.ModelForm |
2. Create View | Use form.is_valid() and process cleaned_data |
3. Render Template | Use {{ form.as_p }} or custom HTML |
4. Handle POST | Use request.POST and request.FILES |
5. Validate | Use clean_<field>() and clean() for cross-field logic |
Conclusion
Django's form framework is flexible and powerful, allowing you to handle user input securely with minimal boilerplate. Whether you're working with custom forms or model-backed forms, Django provides all the tools you need for data validation and display.