How to style forms in Django using Widget Tweaks?

In this article, we are going to explore how to utilize the power of Widget Tweaks to style our forms in Django. If you have prior experience with Django, you probably know that additional work is often needed to style forms as desired. We are going to set up a simple Contact Us form for our example and implement floating labels with the help of Bootstrap 5 and the package Widget Tweaks.

What is Django Widget Tweaks?

Widget Tweaks is a package that provides additional functionalities and features for working with forms in Django. It is not a part of the Django core framework but is instead a third-party package that extends Django's capabilities. It allows us to modify forms directly in our templates.

Let's have a look at the code so far. We have created a new app called Contact which is where the majority of our code is going to live.

The model

In our models.py file we need to create our model with the following fields:

from Django.db import models


class Contact(models.Model):
    first_name = models.CharField(max_length=200, blank=False, null=True)
    last_name = models.CharField(max_length=200, blank=False, null=True)
    email = models.EmailField(max_length=300, blank=False, null=True)
    phone = models.CharField(max_length=100, blank=True, null=True)
    subject = models.CharField(max_length=200, blank=False, null=False)
    message = models.TextField()

    def __str__(self):
        return self.subject
Create a form from our model

Then we need to create a file called forms.py and define the ContactForm class

from Django.forms import ModelForm
from .models import Contact


class ContactForm(ModelForm):
    class Meta:
        model = Contact
        fields = '__all__'
Render the form

In our views.py file we are going to create a function that will render our template and pass in the form to the template.

from Django.shortcuts import render
from .forms import ContactForm


def contact(request):
    form = ContactForm()

    context = {'form': form}
    return render(request, 'contact/contact.html', context)
The template

Now let's see the results if we render the form as standard. Please note that we are using Bootstrap 5 in this template.

    <section class="contact">
        <div class="container">
            <div class="row justify-content-center">
                <div class="col-10 text-center my-5 p-4 shadow-container">
                    <h1>Contact Us</h1>
                    <form method="POST">
                        { % csrf_token % }
                        { {form} }
                        <div class="form-group">
                            <button type="submit" class="btn btn-success">Send</button>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </section>

As you can see the results require some styling. If you are using an older version of Django the results may appear slightly different.

form

Setting up Django Widget Tweaks

  1. To set up Django Widget Tweaks, first we need to install the package by running pip install django-widget-tweaks in our terminal.

  2. Next we need to add widget_tweaks to INSTALLED_APPS in our project's settings.py file:

INSTALLED_APPS = [
    # your apps
    'widget_tweaks',
]
  1. Finally we need to load the package into our template by adding {% load widget_tweaks %} at the top of the contact.html file. We are now ready to use Widget Tweaks for our form.
Using Widget Tweaks
  1. Rendering a field and adding a class

The syntax is very simple, we want to render a field from our form, called first_name and then we can add the class.

<label for="id_first_name">First Name:*</label>
{ % render_field form.first_name class="form-control" % }

This will return the following HTML

<label for="id_first_name">First Name:*</label>
<input type="text" name="first_name" maxlength="200" class="form-control" required id="id_first_name">
  1. Adding an attribute

It is very simple to add an attribute just by adding |attr:""

<label for="id_first_name">First Name:*</label>
{ % render_field form.first_name|attr:"title:Hello, world!" class="form-control" % }

This is the HTML output and as you can see the title is present.

<label for="id_first_name">First Name:*</label>
<input type="text" name="first_name" maxlength="200" class="form-control" title="Hello, world!" required id="id_first_name">
  1. Looping through the fields

We can loop through all of the fields in a form and add the required class and attribute as in the example below. We are also rendering errors specific to the field.

{ % for field in form.visible_fields % }

<div class="form-floating mb-4">
    { { field|add_class:'form-control'|attr:'placeholder = field.label.text' } }
    <label for="{ { field.id_for_label } }">{ { field.label } }</label>

    { % for error in field.errors % }
    <span class="text-danger">{ { error } }</span>
    { % endfor % }
</div>

{ % endfor % }
  1. Rendering Errors
  • Field-related errors We can render errors specific to individual field by simply adding the following piece of code.
{ % for error in form.first_name.errors % }
<span class="text-danger">{ { error } }</span>
{ % endfor % }
  • Rendering general form errors Sometimes we may need to catch generic errors like 'Too Many Login Attempts'. Just add this to the end of the form.
  { % if form.non_field_errors % }
  <div>
    <span class="text-danger"> { { form.non_field_errors  } }</span>
  </div>
  { % endif % }
  1. Rendering hidden fields.

Sometimes we need to include data that is not displayed to the user but is sent along with the form submission and this is where hidden fields come in handy. To render hidden fields, we can use the below example.

{ % for hidden in form.hidden_fields % }
{ { hidden } }
{ % endfor % }

Floating Label Form Rendering.

Now with this knowledge, let's finish our form. We will be using the Bootstrap 5 syntax, which means we need to render our field first before the label to achieve the floating label effect. The field must have a form-floating class and a placeholder for this to work. For greater control, we are going to render each field individually rather than looping through.

 <form method="POST">
                    { % csrf_token % }
                    <!-- If any hidden fields -->
                    { % for hidden in form.hidden_fields % }
                    { { hidden } }
                    { % endfor % }
                    <!-- end -->

                    <div class="form-floating mb-2">
                        { % render_field form.first_name class="form-control" placeholder=form.text.label% }
                        <label for="id_first_name">First Name:*</label>
                        <!-- Rendering field errors -->
                        { % for error in form.first_name.errors % }
                        <span class="text-danger">{ { error } }</span>
                        { % endfor % }
                    </div>

                    <div class="form-floating mb-2">
                        { % render_field form.last_name class="form-control" placeholder=form.text.label % }
                        <label for="id_last_name">Last Name:*</label>
                        <!-- Rendering field errors -->
                        { % for error in form.last_name.errors % }
                        <span class="text-danger">{ { error } }</span>
                        { % endfor % }
                    </div>

                    <div class="form-floating mb-2">
                        { % render_field form.email class="form-control" placeholder=form.text.label % }
                        <label for="id_email">Email:*</label>
                        <!-- Rendering field errors -->
                        { % for error in form.email.errors % }
                        <span class="text-danger">{ { error } }</span>
                        { % endfor % }
                    </div>

                    <div class="form-floating mb-2">
                        { % render_field form.phone class="form-control" placeholder=form.text.label % }
                        <label for="id_phone">Phone:</label>
                        <!-- Rendering field errors -->
                        { % for error in form.phone.errors % }
                        <span class="text-danger">{ { error } }</span>
                        { % endfor % }
                    </div>

                    <div class="form-floating mb-2">
                        { % render_field form.subject class="form-control" placeholder=form.text.label % }
                        <label for="id_subject">Subject:*</label>
                        <!-- Rendering field errors -->
                        { % for error in form.subject.errors % }
                        <span class="text-danger">{ { error } }</span>
                        { % endfor % }
                    </div>

                    <div class="form-group mb-4">
                        <label for="id_message" class="form-label">Message:</label>
                        { % render_field form.message|attr:"maxlength:2500" class="form-control" rows="5" % }
                        { % for error in form.message.errors % }
                        <span class="text-danger">{ { error } }</span>
                        { % endfor % }
                    </div>

                    <!-- Rendering Generic Form Errors -->
                    { % if form.non_field_errors % }
                    <div>
                        <span class="text-danger"> { { form.non_field_errors } }</span>
                    </div>
                    { % endif % }

                    <div class="form-group">
                        <button type="submit" class="btn btn-success">Send</button>
                    </div>
                </form>

We have created a modern-looking form by implementing Django Widget Tweaks and Bootstrap 5 in our Django project.

contact form

In conclusion, leveraging Django Widget Tweaks alongside Bootstrap 5 empowers developers to create visually appealing and user-friendly forms within Django projects. This article has demonstrated how Widget Tweaks, as a third-party package, extends Django's capabilities by enabling direct form modifications in templates. By following the step-by-step guide provided, developers can implement features like floating labels, custom classes, attributes, and error handling efficiently. Integrating these tools not only enhances the aesthetic appeal of forms but also improves user experience and responsiveness.

You can find more information about the package here and GitHub Repo To view the code for this article click here

Bootstrap 5FormsDjangoPythonWidget Tweaks
Avatar for Dayana

Written by Dayana

Currently studying Full stack software development. I love coding, cars and music :)

Loading

Fetching comments

Hey! 👋

Got something to say?

or to leave a comment.