Form Component
Django’s form component simplifies HTML form handling by providing automatic field rendering, built-in validation, and error message generation. The component also handles file uploads through the request.FILES interface.
Form File Upload
To upload a file via an HTML form, two requirements must be met:
- The form
methodmust bepost - The form
enctypemust bemultipart/form-data
def ab_file(request):
if request.method == 'POST':
# request.POST only carries ordinary key/value pairs — files are not included
print(request.FILES) # retrieve file data
# Example output:
# <MultiValueDict: {'file': [<InMemoryUploadedFile: example.jpg (image/jpeg)>]}>
file_obj = request.FILES.get('file') # get the file object
print(file_obj.name)
with open(file_obj.name, 'wb') as f:
for chunk in file_obj.chunks():
# chunks() reads the file in pieces (recommended for large files)
f.write(chunk)
return render(request, 'form.html')Frontend template:
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>username: <input type="text" name="username"></p>
<p>file: <input type="file" name="file"></p>
<input type="submit">
</form>Django Form Class
Instead of processing raw request data manually, define a Form class that declares fields and their validation rules:
from django import forms
from django.core.exceptions import ValidationError
class LoginForm(forms.Form):
username = forms.CharField(
min_length=3,
max_length=20,
error_messages={
'required': 'Username is required.',
'min_length': 'Username must be at least 3 characters.',
'max_length': 'Username must be at most 20 characters.',
}
)
password = forms.CharField(
min_length=6,
widget=forms.PasswordInput,
error_messages={
'required': 'Password is required.',
'min_length': 'Password must be at least 6 characters.',
}
)
email = forms.EmailField(
error_messages={
'required': 'Email is required.',
'invalid': 'Enter a valid email address.',
}
)Using a Form in a View
def login(request):
if request.method == 'POST':
form = LoginForm(data=request.POST)
if form.is_valid():
# All fields passed validation
cleaned = form.cleaned_data # dict of validated values
print(cleaned)
# {'username': 'jason', 'password': '123456', 'email': '[email protected]'}
return redirect('/home/')
# Validation failed — re-render the form with error messages
else:
form = LoginForm() # empty form for GET requests
return render(request, 'login.html', {'form': form})Rendering the Form in a Template
<form action="" method="post" novalidate>
{% csrf_token %}
{% for field in form %}
<div>
<label>{{ field.label }}</label>
{{ field }}
{% if field.errors %}
<span class="error">{{ field.errors.0 }}</span>
{% endif %}
</div>
{% endfor %}
<button type="submit">Submit</button>
</form>Add
novalidate to the <form> tag to disable the browser’s built-in HTML5 validation and let Django handle all validation server-side, keeping the error messages consistent.Custom Validation
Two ways to add field-level custom validation:
Using validators
import re
from django.core.exceptions import ValidationError
def check_username(value):
if re.search(r'\d', value):
raise ValidationError('Username must not contain digits.')
class RegisterForm(forms.Form):
username = forms.CharField(validators=[check_username])Using clean_<fieldname> methods
class RegisterForm(forms.Form):
username = forms.CharField()
password = forms.CharField(widget=forms.PasswordInput)
confirm_password = forms.CharField(widget=forms.PasswordInput)
def clean_username(self):
value = self.cleaned_data.get('username')
if value.lower() == 'admin':
raise ValidationError('The username "admin" is not allowed.')
return value
def clean(self):
# Cross-field validation
password = self.cleaned_data.get('password')
confirm = self.cleaned_data.get('confirm_password')
if password and confirm and password != confirm:
raise ValidationError('The two passwords do not match.')
return self.cleaned_dataLast updated on