Django Template Forms
Forms are an essential part of web applications, allowing users to input data that can be processed by the server. Django provides powerful tools for creating, validating, and rendering HTML forms. In this tutorial, we'll explore how to work with forms in Django templates.
Introduction to Django Forms
Django forms provide a convenient way to handle user input in web applications. They handle three main aspects of form processing:
- Rendering HTML form elements (inputs, labels, etc.)
- Validating submitted data (ensuring data meets requirements)
- Converting data to Python types for processing
The Django template system works seamlessly with Django's form classes to display forms in your HTML templates with minimal effort.
Creating a Basic Django Form
Let's start by creating a simple contact form. First, we need to define our form class in a file called forms.py
within your app directory:
# myapp/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)
This creates a form with three fields: name, email, and message. Django will automatically handle validation for each field type.
Rendering Forms in Templates
Simple Form Rendering
Now let's create a view that passes this form to a template:
# myapp/views.py
from django.shortcuts import render
from .forms import ContactForm
def contact_view(request):
form = ContactForm()
return render(request, 'contact.html', {'form': form})
In your template, you can render the form with just a few lines:
<!-- templates/contact.html -->
<form method="POST">
{% csrf_token %}
{{ form }}
<button type="submit">Send</button>
</form>
The {{ form }}
template variable will render all form fields and their labels. The output will look something like this:
<tr>
<th><label for="id_name">Name:</label></th>
<td><input type="text" name="name" maxlength="100" required id="id_name"></td>
</tr>
<tr>
<th><label for="id_email">Email:</label></th>
<td><input type="email" name="email" required id="id_email"></td>
</tr>
<tr>
<th><label for="id_message">Message:</label></th>
<td><textarea name="message" required id="id_message"></textarea></td>
</tr>
Form Rendering Options
Django provides several ways to render forms in templates:
1. Render the entire form at once
{{ form }}
This renders the form as an HTML table by default.
2. Render as paragraphs
{{ form.as_p }}
This wraps each field in a <p>
tag:
<p>
<label for="id_name">Name:</label>
<input type="text" name="name" maxlength="100" required id="id_name">
</p>
<!-- and so on for other fields -->
3. Render as list items
{{ form.as_ul }}
This renders fields as list items:
<li>
<label for="id_name">Name:</label>
<input type="text" name="name" maxlength="100" required id="id_name">
</li>
<!-- and so on for other fields -->
4. Render as a table
{{ form.as_table }}
This is the default rendering method, with each field as a table row.
Manual Form Field Rendering
For more control over your form's appearance, you can render each field individually:
<form method="POST">
{% csrf_token %}
<div class="form-group">
<label for="{{ form.name.id_for_label }}">Your Name</label>
{{ form.name }}
{% if form.name.errors %}
<div class="error">{{ form.name.errors }}</div>
{% endif %}
</div>
<div class="form-group">
<label for="{{ form.email.id_for_label }}">Email Address</label>
{{ form.email }}
{% if form.email.errors %}
<div class="error">{{ form.email.errors }}</div>
{% endif %}
</div>
<div class="form-group">
<label for="{{ form.message.id_for_label }}">Your Message</label>
{{ form.message }}
{% if form.message.errors %}
<div class="error">{{ form.message.errors }}</div>
{% endif %}
</div>
<button type="submit">Send</button>
</form>
This approach gives you complete control over the form's structure and styling.
Processing Form Submissions
Let's modify our view to handle form submissions:
# myapp/views.py
from django.shortcuts import render, redirect
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']
# Do something with the data (e.g., send email)
# ...
return redirect('success')
else:
form = ContactForm()
return render(request, 'contact.html', {'form': form})
In our template, we can display validation errors:
<form method="POST">
{% csrf_token %}
{% if form.non_field_errors %}
<div class="error-messages">
{{ form.non_field_errors }}
</div>
{% endif %}
{{ form.as_p }}
<button type="submit">Send</button>
</form>
Working with ModelForms
If your form corresponds to a Django model, you can use ModelForm
to automatically create a form from your model:
# models.py
from django.db import models
class Contact(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField()
message = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
# forms.py
from django import forms
from .models import Contact
class ContactModelForm(forms.ModelForm):
class Meta:
model = Contact
fields = ['name', 'email', 'message']
# or use fields = '__all__' for all fields
ModelForms work the same way in templates, but they automatically save to the database:
# views.py
from django.shortcuts import render, redirect
from .forms import ContactModelForm
def contact_view(request):
if request.method == 'POST':
form = ContactModelForm(request.POST)
if form.is_valid():
# This automatically creates a Contact model instance
contact = form.save()
return redirect('success')
else:
form = ContactModelForm()
return render(request, 'contact.html', {'form': form})
Customizing Form Widgets
You can customize the HTML elements used for your form fields by specifying widgets:
class ContactForm(forms.Form):
name = forms.CharField(
max_length=100,
widget=forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Your name'
})
)
email = forms.EmailField(
widget=forms.EmailInput(attrs={
'class': 'form-control',
'placeholder': '[email protected]'
})
)
message = forms.CharField(
widget=forms.Textarea(attrs={
'class': 'form-control',
'rows': 5,
'placeholder': 'Your message here'
})
)
This adds CSS classes and placeholders to your form fields, which can be styled with CSS.
Practical Example: Registration Form
Let's create a more complex registration form with custom validation:
# forms.py
from django import forms
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
class RegistrationForm(forms.Form):
username = forms.CharField(max_length=150)
email = forms.EmailField()
password = forms.CharField(widget=forms.PasswordInput)
password_confirm = forms.CharField(widget=forms.PasswordInput)
def clean_username(self):
username = self.cleaned_data.get('username')
if User.objects.filter(username=username).exists():
raise ValidationError('This username is already taken.')
return username
def clean(self):
cleaned_data = super().clean()
password = cleaned_data.get('password')
password_confirm = cleaned_data.get('password_confirm')
if password and password_confirm and password != password_confirm:
self.add_error('password_confirm', 'Passwords do not match.')
return cleaned_data
View for processing the registration form:
# views.py
from django.shortcuts import render, redirect
from django.contrib.auth.models import User
from .forms import RegistrationForm
def register_view(request):
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
# Create a new user
User.objects.create_user(
username=form.cleaned_data['username'],
email=form.cleaned_data['email'],
password=form.cleaned_data['password']
)
return redirect('login')
else:
form = RegistrationForm()
return render(request, 'register.html', {'form': form})
And the template:
<!-- templates/register.html -->
<h2>Register</h2>
<form method="POST">
{% csrf_token %}
{% for field in form %}
<div class="form-group">
{{ field.label_tag }}
{{ field }}
{% if field.errors %}
<div class="error">
{% for error in field.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% endif %}
{% if field.help_text %}
<small class="form-text text-muted">{{ field.help_text }}</small>
{% endif %}
</div>
{% endfor %}
<button type="submit">Register</button>
</form>
Form Layout with Crispy Forms
For better form layouts, you can use third-party packages like django-crispy-forms
. First, install it:
pip install django-crispy-forms
Add it to your INSTALLED_APPS
in settings.py
:
INSTALLED_APPS = [
# ...
'crispy_forms',
]
CRISPY_TEMPLATE_PACK = 'bootstrap4' # or 'bootstrap5', etc.
Then load and use it in your templates:
{% load crispy_forms_tags %}
<form method="POST">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-primary">Submit</button>
</form>
This automatically generates Bootstrap-styled forms with appropriate structure and classes.
Form Sets
Django also provides form sets for handling multiple copies of the same form:
# views.py
from django.forms import formset_factory
from .forms import ContactForm
def contact_list_view(request):
ContactFormSet = formset_factory(ContactForm, extra=3)
if request.method == 'POST':
formset = ContactFormSet(request.POST)
if formset.is_valid():
for form in formset:
if form.has_changed(): # Only process forms that have data
# Process form data
name = form.cleaned_data['name']
email = form.cleaned_data['email']
message = form.cleaned_data['message']
# ...
return redirect('success')
else:
formset = ContactFormSet()
return render(request, 'contact_list.html', {'formset': formset})
And in your template:
<form method="POST">
{% csrf_token %}
{{ formset.management_form }}
{% for form in formset %}
<div class="contact-form">
<h3>Contact #{{ forloop.counter }}</h3>
{{ form.as_p }}
</div>
{% endfor %}
<button type="submit">Submit All</button>
</form>
Summary
Django's template forms system provides a powerful way to create, validate, and process HTML forms. Key points to remember:
- Define forms in a
forms.py
file usingforms.Form
orforms.ModelForm
- Render forms in templates using
{{ form }}
,{{ form.as_p }}
, or by rendering fields individually - Process form submissions in views with
form.is_valid()
and access data throughform.cleaned_data
- Use widgets to customize the HTML of form fields
- Add custom validation with
clean_fieldname()
andclean()
methods - Consider using third-party packages like
django-crispy-forms
for better styling
By mastering Django template forms, you can create interactive and user-friendly web applications that securely process user input.
Additional Resources
- Django Forms Documentation
- Django ModelForms Documentation
- Django Form Field Widgets
- Django Crispy Forms Documentation
Exercises
- Create a blog post form with title, content, and tags fields
- Build a multi-step form using Django sessions to store interim data
- Create a form that uploads files and displays a preview
- Implement client-side validation using Django form errors and JavaScript
- Create a dynamic form where fields are added or removed based on user actions
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)