Django's forms framework handles HTML form rendering, data validation, and security. It protects against common vulnerabilities like CSRF attacks.
Why Use Django Forms?
- Automatic HTML generation: Create form fields with proper attributes
- Validation: Built-in and custom validation rules
- Security: CSRF protection included
- Error handling: Display validation errors easily
- Data cleaning: Normalize and sanitize input
Creating a Form Class
Forms are defined as Python classes that inherit from forms.Form. Each class attribute represents a form field with its own validation rules.
python# polls/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) # Optional fields phone = forms.CharField(max_length=20, required=False) subscribe = forms.BooleanField(required=False)
Form Field Types
| Field Type | HTML Input | Description |
|---|---|---|
CharField | <input type="text"> | Single-line text |
EmailField | <input type="email"> | Email with validation |
IntegerField | <input type="number"> | Integer numbers |
FloatField | <input type="number"> | Decimal numbers |
BooleanField | <input type="checkbox"> | True/False |
ChoiceField | <select> | Dropdown selection |
DateField | <input type="text"> | Date input |
FileField | <input type="file"> | File upload |
URLField | <input type="url"> | URL with validation |
Using Forms in Views
In a view, you create a form instance for GET requests (empty form) and bind POST data to it for validation on submission.
python# polls/views.py from django.shortcuts import render, redirect from .forms import ContactForm def contact(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'] # Send email, save to database, etc. return redirect('success') else: form = ContactForm() return render(request, 'contact.html', {'form': form})
Rendering Forms in Templates
The simplest way to render a form is to use Django's built-in rendering methods inside a <form> tag with a CSRF token.
html<!-- templates/contact.html --> <form method="post"> {% csrf_token %} {{ form.as_p }} <button type="submit">Send</button> </form>
Rendering Options
Django provides several methods that render the entire form with different HTML wrappers.
html{{ form.as_p }} <!-- Wraps each field in <p> --> {{ form.as_div }} <!-- Wraps each field in <div> (default since Django 5.0) --> {{ form.as_ul }} <!-- Wraps each field in <li> --> {{ form.as_table }} <!-- Renders as <tr> (needs <table>) -->
Manual Field Rendering
For full control over HTML:
html<form method="post"> {% csrf_token %} <div class="field"> <label for="{{ form.name.id_for_label }}">Name:</label> {{ form.name }} {% if form.name.errors %} <span class="error">{{ form.name.errors.0 }}</span> {% endif %} </div> <div class="field"> <label for="{{ form.email.id_for_label }}">Email:</label> {{ form.email }} {{ form.email.errors }} </div> <button type="submit">Send</button> </form>
CSRF Protection
Always include {% csrf_token %} in your forms. Django uses this token to prevent Cross-Site Request Forgery attacks:
html<form method="post"> {% csrf_token %} <!-- Required for POST forms --> ... </form>
Without this token, Django will reject the form submission with a 403 Forbidden error.
Common Pitfalls
- Forgetting
{% csrf_token %}: Without the CSRF token, Django rejects POST form submissions with a 403 Forbidden error. - Accessing
cleaned_databeforeis_valid(): You must callform.is_valid()first. Accessingcleaned_dataon an unvalidated form raises anAttributeError. - Not re-rendering the form on validation errors: When
is_valid()returnsFalse, render the same template with the form object to display error messages to the user.
Best Practices
- Always use POST for forms that modify data: GET should only be used for search forms or read-only operations.
- Use
form.as_div(Django 5.0+) or manual field rendering for production-quality HTML styling. - Redirect after successful POST: Follow the Post/Redirect/Get pattern to prevent duplicate form submissions.
Summary
- Django forms handle rendering, validation, and CSRF protection automatically
- Define form fields in a
forms.Formsubclass with field types likeCharField,EmailField,BooleanField - Check
request.method == 'POST'and callform.is_valid()before accessingcleaned_data - Always include
{% csrf_token %}in form templates to prevent cross-site request forgery - Render forms with
{{ form.as_p }},{{ form.as_div }}, or manual field-by-field rendering
Code Examples
from django import forms
class ContactForm(forms.Form):
name = forms.CharField(max_length=100)
email = forms.EmailField()
message = forms.CharField(widget=forms.Textarea)
# In a view
def contact(request):
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
name = form.cleaned_data['name']
# Process data...
else:
form = ContactForm()
return render(request, 'contact.html', {'form': form})