Subject

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.

1. What Is 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.py

The role of forms.py is to contain form definitions such as:

This keeps your project organized and separates form logic from views.

2. Why Use Django Forms?

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.

3. Creating Your First Form in forms.py

Create a file named forms.py inside your app.

blog/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)

Explanation

This form is not connected to a model. It is a manual Django form.

4. Using the Form in a View

Now use the form in views.py.

blog/views.py

from 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.

5. Displaying the Form in a Template

blog/templates/blog/contact.html

<h1>Contact Us</h1>

<form method="POST">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">Send</button>
</form>

What {{ form.as_p }} does

It displays the form fields wrapped in paragraph tags.

Django also provides:

But as_p is often the simplest for beginners.

6. Common Form Fields in Django

Django provides many field types.

Example

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()

Common field types

7. Using Labels

By default, Django creates labels from field names. But you can customize them.

Example

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)

Why use custom labels?

Custom labels make forms more user-friendly and professional.

Instead of:

You can display:

8. Using Widgets

Widgets 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.

Example

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
        })
    )

What this changes

9. Common Widgets

Some common widgets are:

Example

password = forms.CharField(widget=forms.PasswordInput)

This hides the entered password in the browser.

10. Handling Form Submission

Now let us process submitted data.

views.py

from 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})

Explanation

11. What Is cleaned_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

 

Example

if form.is_valid():
    name = form.cleaned_data['name']
    email = form.cleaned_data['email']
    message = form.cleaned_data['message']

Why it matters

You should use cleaned_data, not raw request.POST, when working with validated form input.

12. Example of Showing Submitted Data

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.

13. Basic Built-In Validation

Django automatically validates many things for you.

Example

class ContactForm(forms.Form):
    name = forms.CharField(max_length=10)
    email = forms.EmailField()

Automatic checks

If something is wrong, Django shows errors automatically.

14. Displaying Errors in Templates

If the submitted data is invalid, errors can be shown in the template.

Example

<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.

15. Custom Validation Methods

Sometimes built-in validation is not enough. Django allows you to define your own validation methods.

There are two main ways:

16. Validating a Single Field with clean_fieldname

Suppose you want to reject a certain name.

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)

    def clean_name(self):
        name = self.cleaned_data['name']

        if name.lower() == 'admin':
            raise forms.ValidationError("This name is not allowed.")

        return name

How it works

17. Validating the Whole Form with clean()

Use clean() when validation depends on multiple fields together.

Example

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_data

Why use clean()?

Because it checks the form as a whole, not just one field.

18. Adding Initial Values

You can provide default values when defining fields.

Example

class FeedbackForm(forms.Form):
    name = forms.CharField(initial='Guest')
    message = forms.CharField(widget=forms.Textarea)

This can be useful for prefilled forms.

19. Using Choice Fields

You can create dropdown menus using ChoiceField.

Example

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.

20. Example: A Complete Feedback Form

forms.py

from 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 name

This example includes:

21. Example View for the Feedback Form

views.py

from 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})

22. Example Template for the Feedback Form

feedback.html

<h1>Feedback Form</h1>

<form method="POST">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">Submit</button>
</form>

23. Common Beginner Mistakes

Mistake 1: Forgetting request.POST

Wrong:

form = ContactForm()

inside a POST request.

Correct:

form = ContactForm(request.POST)

Mistake 2: Forgetting 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']

Mistake 3: Forgetting to return cleaned value

In clean_name(), always return the value if valid.

Mistake 4: Forgetting {% csrf_token %}

Without it, Django will reject POST requests.

Mistake 5: Confusing forms.Form and forms.ModelForm

This tutorial focuses on forms.Form inside forms.py.

24. Best Practices

25. Beginner Analogy

Think of a Django form as a receptionist at an office.

That is exactly what Django forms do.

26. Summary

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.

27. Key Takeaways

28. Small Knowledge Check

  1. What is the role of forms.py in Django?
  2. What is the difference between forms.Form and forms.ModelForm?
  3. What is a widget in Django forms?
  4. When can you use cleaned_data?
  5. What does clean_name() do?
  6. When should you use clean() instead of clean_fieldname()?

29. Conclusion

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.