Django Form Templates
When working with Django forms, one of the most powerful features is how easily they integrate with Django's template system. This integration allows you to render forms as HTML, customize their appearance, and handle user input elegantly.
Introduction to Django Form Templates
Django forms provide a bridge between your Python code and HTML interfaces. While you can create forms in pure HTML, Django forms offer several advantages:
- Automatic validation
- Easy rendering
- Consistent styling
- CSRF protection
- Built-in error handling
In this tutorial, we'll explore how to display Django forms in templates and customize their appearance to create user-friendly interfaces.
Basic Form Rendering
Let's start with the most basic way to render a form in a template. First, let's define a simple form in forms.py
:
# 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)
Now, in your view, you'll pass this form to the 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 entire form with just a few lines:
<!-- templates/contact.html -->
<form method="post">
{% csrf_token %}
{{ form }}
<button type="submit">Submit</button>
</form>
This will render the form with the default styling, which includes:
- Labels for each field
- Input elements with appropriate HTML types
- Any validation attributes
Form Rendering Options
Django provides several ways to render forms in templates:
1. Rendering the entire form at once
{{ form }}
Output:
<label for="id_name">Name:</label>
<input type="text" name="name" maxlength="100" required id="id_name">
<label for="id_email">Email:</label>
<input type="email" name="email" required id="id_email">
<label for="id_message">Message:</label>
<textarea name="message" required id="id_message"></textarea>
2. Rendering as a table
{{ form.as_table }}
Output:
<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>
Remember to wrap this in a <table>
element in your template:
<form method="post">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<button type="submit">Submit</button>
</form>
3. Rendering as paragraphs
{{ form.as_p }}
Output:
<p>
<label for="id_name">Name:</label>
<input type="text" name="name" maxlength="100" required id="id_name">
</p>
<p>
<label for="id_email">Email:</label>
<input type="email" name="email" required id="id_email">
</p>
<p>
<label for="id_message">Message:</label>
<textarea name="message" required id="id_message"></textarea>
</p>
4. Rendering as an unordered list
{{ form.as_ul }}
Output:
<li>
<label for="id_name">Name:</label>
<input type="text" name="name" maxlength="100" required id="id_name">
</li>
<li>
<label for="id_email">Email:</label>
<input type="email" name="email" required id="id_email">
</li>
<li>
<label for="id_message">Message:</label>
<textarea name="message" required id="id_message"></textarea>
</li>
Remember to wrap this in a <ul>
element in your template:
<form method="post">
{% csrf_token %}
<ul>
{{ form.as_ul }}
</ul>
<button type="submit">Submit</button>
</form>
Rendering Individual Fields
For more control over the layout, you can render fields 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 }}">Your Email:</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 Message</button>
</form>
This approach gives you complete control over:
- HTML structure around each field
- Custom labels
- Error message placement and styling
- Adding additional elements or instructions
Looping Through Form Fields
For consistent styling with less repetition, you can loop through all fields:
<form method="post">
{% csrf_token %}
{% for field in form %}
<div class="form-group">
<label for="{{ field.id_for_label }}">{{ field.label }}:</label>
{{ field }}
{% if field.help_text %}
<small class="form-text text-muted">{{ field.help_text }}</small>
{% endif %}
{% if field.errors %}
<div class="error">{{ field.errors }}</div>
{% endif %}
</div>
{% endfor %}
<button type="submit">Submit</button>
</form>
Styling Django Forms
Django forms come with minimal default styling. Here are some approaches to style them:
1. Adding CSS Classes
You can add CSS classes when defining the form:
class ContactForm(forms.Form):
name = forms.CharField(
max_length=100,
widget=forms.TextInput(attrs={'class': 'form-control'})
)
email = forms.EmailField(
widget=forms.EmailInput(attrs={'class': 'form-control'})
)
message = forms.CharField(
widget=forms.Textarea(attrs={'class': 'form-control', 'rows': 5})
)
2. Using a Form Renderer
Django's form renderer system allows you to customize how forms are rendered globally:
# settings.py
FORM_RENDERER = 'django.forms.renderers.TemplatesSetting'
# Then create templates in templates/django/forms/widgets/
3. Using CSS Frameworks
Here's how to integrate Django forms with Bootstrap:
<form method="post">
{% csrf_token %}
{% for field in form %}
<div class="form-group mb-3">
<label for="{{ field.id_for_label }}" class="form-label">
{{ field.label }}
</label>
{{ field|add_class:"form-control" }}
{% if field.help_text %}
<small class="form-text text-muted">{{ field.help_text }}</small>
{% endif %}
{% if field.errors %}
{% for error in field.errors %}
<div class="invalid-feedback d-block">{{ error }}</div>
{% endfor %}
{% endif %}
</div>
{% endfor %}
<button type="submit" class="btn btn-primary">Submit</button>
</form>
Note: The add_class
filter requires the django-widget-tweaks
package:
pip install django-widget-tweaks
Then add it to your INSTALLED_APPS
:
# settings.py
INSTALLED_APPS = [
# ...
'widget_tweaks',
# ...
]
Handling Form Submission
Let's complete the picture by handling form submission:
# 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 your template, you'll now need to display validation errors:
<form method="post">
{% csrf_token %}
{% if form.non_field_errors %}
<div class="alert alert-danger">
{% for error in form.non_field_errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
{% for field in form %}
<div class="form-group">
<label for="{{ field.id_for_label }}">{{ field.label }}:</label>
{{ field }}
{% if field.errors %}
<div class="error">
{% for error in field.errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
</div>
{% endfor %}
<button type="submit">Submit</button>
</form>
Real-World Example: User Registration Form
Let's create a complete user registration form with validation and styling:
# forms.py
from django import forms
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
class UserRegistrationForm(forms.Form):
username = forms.CharField(
max_length=150,
widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Choose a username'})
)
email = forms.EmailField(
widget=forms.EmailInput(attrs={'class': 'form-control', 'placeholder': 'Enter your email'})
)
password1 = forms.CharField(
label="Password",
widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': 'Create a password'})
)
password2 = forms.CharField(
label="Confirm Password",
widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': 'Repeat your password'})
)
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_email(self):
email = self.cleaned_data.get('email')
if User.objects.filter(email=email).exists():
raise ValidationError("This email is already registered.")
return email
def clean(self):
cleaned_data = super().clean()
password1 = cleaned_data.get('password1')
password2 = cleaned_data.get('password2')
if password1 and password2 and password1 != password2:
self.add_error('password2', "The two password fields didn't match.")
return cleaned_data
View:
# views.py
from django.shortcuts import render, redirect
from django.contrib.auth.models import User
from django.contrib.auth import login
from .forms import UserRegistrationForm
def register(request):
if request.method == 'POST':
form = UserRegistrationForm(request.POST)
if form.is_valid():
user = User.objects.create_user(
username=form.cleaned_data['username'],
email=form.cleaned_data['email'],
password=form.cleaned_data['password1']
)
login(request, user)
return redirect('home')
else:
form = UserRegistrationForm()
return render(request, 'register.html', {'form': form})
Template:
<!-- templates/register.html -->
{% extends 'base.html' %}
{% block content %}
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h2 class="text-center">Create an Account</h2>
</div>
<div class="card-body">
<form method="post">
{% csrf_token %}
{% if form.non_field_errors %}
<div class="alert alert-danger">
{% for error in form.non_field_errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
{% for field in form %}
<div class="mb-3">
<label for="{{ field.id_for_label }}" class="form-label">
{{ field.label }}
</label>
{{ field }}
{% if field.errors %}
{% for error in field.errors %}
<div class="text-danger">{{ error }}</div>
{% endfor %}
{% endif %}
{% if field.help_text %}
<small class="form-text text-muted">{{ field.help_text }}</small>
{% endif %}
</div>
{% endfor %}
<div class="d-grid gap-2">
<button type="submit" class="btn btn-primary">Register</button>
</div>
</form>
</div>
<div class="card-footer text-center">
<p>Already have an account? <a href="{% url 'login' %}">Login</a></p>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
Summary
Django forms and templates work together to create a powerful system for handling user input:
- Form Definition: Create form classes in Python that define fields and validation rules.
- Template Integration: Pass form instances to templates from views.
- Rendering Options: Choose between automatic rendering (
{{ form }}
,{{ form.as_p }}
, etc.) or manual field rendering for more control. - Customization: Style forms using CSS, widget attributes, or third-party libraries.
- Validation: Handle form submission and display error messages in your templates.
This integration lets you build complex, user-friendly forms with minimal code while maintaining strong validation and security features.
Additional Resources and Exercises
Resources
- Django Forms Documentation
- Django Form Field Widgets
- Django Crispy Forms - A popular package for beautiful form rendering
Exercises
- Basic Form: Create a contact form that saves messages to a database model.
- Custom Styling: Apply custom CSS to make a form match your site's design.
- Complex Form: Build a multi-step form using Django's form wizard.
- Form with File Upload: Create a form that includes file uploads and displays previews.
- Form with Dynamic Fields: Create a form where some fields appear or disappear based on other field values using JavaScript.
By mastering Django form templates, you'll be able to create intuitive, user-friendly interfaces for your web applications while maintaining strong validation and security.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)