Django forms, widgets, labels, cleaned data, and validation methods.
Django forms are one of the most useful tools for building interactive websites. They help you collect user input, validate it, display errors clearly, and process clean data safely. While ModelForm is very convenient when working with database models, learning how to build forms manually with forms.py is extremely important because it gives you more control and helps you understand how Django forms really work.
In this tutorial, you will learn how to create forms in forms.py, use different field types, customize labels and widgets, access cleaned data, and apply custom validation methods.
forms.py?In Django, forms.py is a file where you define form classes for your app.
For example, inside your app folder:
blog/
├── forms.py
├── views.py
├── models.pyThe role of forms.py is to contain form definitions such as:
This keeps your project organized and separates form logic from views.
You could write plain HTML forms manually, but Django forms give you many advantages:
In short, Django forms make forms easier to build and safer to process.
forms.pyCreate a file named forms.py inside your app.
blog/forms.pyfrom django import forms
class ContactForm(forms.Form):
name = forms.CharField(max_length=100)
email = forms.EmailField()
message = forms.CharField(widget=forms.Textarea)forms.Form is the base class for regular formsname is a text fieldemail is an email fieldmessage is a larger textarea fieldThis form is not connected to a model. It is a manual Django form.
Now use the form in views.py.
blog/views.pyfrom django.shortcuts import render
from .forms import ContactForm
def contact(request):
form = ContactForm()
return render(request, 'blog/contact.html', {'form': form})This creates an empty form and sends it to the template.
blog/templates/blog/contact.html<h1>Contact Us</h1>
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Send</button>
</form>
{{ form.as_p }} doesIt displays the form fields wrapped in paragraph tags.
Django also provides:
{{ form.as_table }}{{ form.as_ul }}But as_p is often the simplest for beginners.
Django provides many field types.
from django import forms
class ExampleForm(forms.Form):
name = forms.CharField(max_length=100)
age = forms.IntegerField()
email = forms.EmailField()
birth_date = forms.DateField()
agree = forms.BooleanField()
CharField → textEmailField → email addressesIntegerField → whole numbersFloatField → decimal numbersDateField → datesBooleanField → true/false checkboxChoiceField → dropdown optionsBy default, Django creates labels from field names. But you can customize them.
from django import forms
class ContactForm(forms.Form):
name = forms.CharField(label='Your Full Name', max_length=100)
email = forms.EmailField(label='Email Address')
message = forms.CharField(label='Your Message', widget=forms.Textarea)
Custom labels make forms more user-friendly and professional.
Instead of:
NameYou can display:
Your Full NameWidgets control how a form field is rendered in HTML.
For example, a CharField usually becomes an <input type="text">, but you can customize its appearance and attributes with widgets.
from django import forms
class ContactForm(forms.Form):
name = forms.CharField(
max_length=100,
widget=forms.TextInput(attrs={
'placeholder': 'Enter your name',
'class': 'form-control'
})
)
email = forms.EmailField(
widget=forms.EmailInput(attrs={
'placeholder': 'Enter your email',
'class': 'form-control'
})
)
message = forms.CharField(
widget=forms.Textarea(attrs={
'placeholder': 'Write your message',
'class': 'form-control',
'rows': 5
})
)
Some common widgets are:
forms.TextInputforms.EmailInputforms.PasswordInputforms.Textareaforms.Selectforms.CheckboxInputforms.DateInputpassword = forms.CharField(widget=forms.PasswordInput)This hides the entered password in the browser.
Now let us process submitted data.
views.pyfrom django.shortcuts import render
from .forms import ContactForm
def contact(request):
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
data = form.cleaned_data
return render(request, 'blog/success.html', {'data': data})
else:
form = ContactForm()
return render(request, 'blog/contact.html', {'form': form})POST, Django fills the form with submitted dataform.is_valid() checks whether the data is correctform.cleaned_data gives access to validated dataPOST, an empty form is displayedcleaned_data?cleaned_data is one of the most important concepts in Django forms.
After validation succeeds, Django stores safe and validated values inside:
form.cleaned_data
if form.is_valid():
name = form.cleaned_data['name']
email = form.cleaned_data['email']
message = form.cleaned_data['message']You should use cleaned_data, not raw request.POST, when working with validated form input.
success.html<h1>Form Submitted Successfully</h1>
<p>Name: {{ data.name }}</p>
<p>Email: {{ data.email }}</p>
<p>Message: {{ data.message }}</p>This is a simple way to verify that the form works.
Django automatically validates many things for you.
class ContactForm(forms.Form):
name = forms.CharField(max_length=10)
email = forms.EmailField()If something is wrong, Django shows errors automatically.
If the submitted data is invalid, errors can be shown in the template.
<h1>Contact Form</h1>
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Send</button>
</form>
{% if form.errors %}
<div class="errors">
{{ form.errors }}
</div>
{% endif %}
You can also display field-specific errors more precisely.
Sometimes built-in validation is not enough. Django allows you to define your own validation methods.
There are two main ways:
clean_fieldnameSuppose you want to reject a certain name.
forms.pyfrom django import forms
class ContactForm(forms.Form):
name = forms.CharField(max_length=100)
email = forms.EmailField()
message = forms.CharField(widget=forms.Textarea)
def clean_name(self):
name = self.cleaned_data['name']
if name.lower() == 'admin':
raise forms.ValidationError("This name is not allowed.")
return nameclean_name()name fieldValidationErrorclean()Use clean() when validation depends on multiple fields together.
from django import forms
class ContactForm(forms.Form):
name = forms.CharField(max_length=100)
email = forms.EmailField()
message = forms.CharField(widget=forms.Textarea)
def clean(self):
cleaned_data = super().clean()
name = cleaned_data.get('name')
message = cleaned_data.get('message')
if name and message:
if name.lower() in message.lower():
raise forms.ValidationError(
"The message should not contain the same text as the name."
)
return cleaned_dataclean()?Because it checks the form as a whole, not just one field.
You can provide default values when defining fields.
class FeedbackForm(forms.Form):
name = forms.CharField(initial='Guest')
message = forms.CharField(widget=forms.Textarea)This can be useful for prefilled forms.
You can create dropdown menus using ChoiceField.
from django import forms
class FeedbackForm(forms.Form):
CATEGORY_CHOICES = [
('general', 'General'),
('bug', 'Bug Report'),
('feature', 'Feature Request'),
]
category = forms.ChoiceField(choices=CATEGORY_CHOICES)
message = forms.CharField(widget=forms.Textarea)This creates a select box in the browser.
forms.pyfrom django import forms
class FeedbackForm(forms.Form):
name = forms.CharField(
label='Your Name',
max_length=100,
widget=forms.TextInput(attrs={
'placeholder': 'Enter your name',
'class': 'form-control'
})
)
email = forms.EmailField(
label='Your Email',
widget=forms.EmailInput(attrs={
'placeholder': 'Enter your email',
'class': 'form-control'
})
)
category = forms.ChoiceField(
choices=[
('general', 'General'),
('bug', 'Bug Report'),
('feature', 'Feature Request'),
],
label='Category'
)
message = forms.CharField(
label='Message',
widget=forms.Textarea(attrs={
'placeholder': 'Write your feedback',
'class': 'form-control',
'rows': 4
})
)
def clean_name(self):
name = self.cleaned_data['name']
if len(name) < 3:
raise forms.ValidationError("Name must contain at least 3 characters.")
return nameThis example includes:
views.pyfrom django.shortcuts import render
from .forms import FeedbackForm
def feedback(request):
submitted = False
if request.method == 'POST':
form = FeedbackForm(request.POST)
if form.is_valid():
submitted = True
data = form.cleaned_data
return render(request, 'blog/feedback_success.html', {
'data': data,
'submitted': submitted
})
else:
form = FeedbackForm()
return render(request, 'blog/feedback.html', {'form': form})feedback.html<h1>Feedback Form</h1>
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Submit</button>
</form>
request.POSTWrong:
form = ContactForm()inside a POST request.
Correct:
form = ContactForm(request.POST)form.is_valid()You should never use cleaned_data before validation.
Wrong:
name = form.cleaned_data['name']Correct:
if form.is_valid():
name = form.cleaned_data['name']In clean_name(), always return the value if valid.
{% csrf_token %}Without it, Django will reject POST requests.
forms.Form and forms.ModelFormforms.Form is manualforms.ModelForm is connected to a modelThis tutorial focuses on forms.Form inside forms.py.
forms.pyis_valid()cleaned_data instead of raw POST dataclean_fieldname() for field-level validationclean() for form-level validationThink of a Django form as a receptionist at an office.
That is exactly what Django forms do.
In this tutorial, you learned how to create Django forms in forms.py using forms.Form. You saw how to define fields, customize labels, use widgets, process submitted data, access cleaned_data, and apply both built-in and custom validation methods.
These skills are essential for building forms such as contact pages, feedback forms, search forms, login forms, and registration forms.
forms.py stores your form classesforms.Form is used for manual formsform.is_valid() performs validationcleaned_data gives safe validated dataclean_fieldname() validates a single fieldclean() validates the full formforms.py in Django?forms.Form and forms.ModelForm?cleaned_data?clean_name() do?clean() instead of clean_fieldname()?Django forms built with forms.py give you full control over user input. They help you create professional forms, validate data correctly, and process information safely. Once you master these concepts, you are ready to build more advanced form workflows in your Django applications.