Trey Hunner

web development, programming, open source

CSS Classes and Django Form Fields

| Comments

Django forms provide input validation and HTML form field generation. They also integrate nicely with models. However, Django itself does not allow one-off customizations of form-generated HTML.

In this post I will discuss a method for customizing the HTML generated by Django form fields, with the specific goal of adding custom CSS classes to Django form fields.

Here’s a Django form definition:

1
2
3
4
5
from django import forms

class AuthenticationForm(forms.Form):
    username = forms.CharField(max_length=254)
    password = forms.CharField(widget=forms.PasswordInput)

Here’s the form used in a template:

{{ form.as_p }}

The Problem

We’re using Bootstrap and we want to add an input-lg CSS class onto our username field to make it really big.

The Solution(s)

There are many ways to solve this problem. I will discuss some solutions I dislike before I discuss my preferred solution.

Using a form widget attribute

We could add a class attribute to our Django form field:

1
2
3
4
5
6
7
8
from django import forms

class AuthenticationForm(forms.Form):
    username = forms.CharField(
        max_length=254,
        widget=forms.TextInput(attrs={'class': "input-lg"}),
    )
    password = forms.CharField(widget=forms.PasswordInput)

I dislike this approach because it requires including presentation rules in our back-end code. This class attribute is used exclusively by our CSS and/or JavaScript and should therefore live in Django templates, not in Python code.

Using django-floppyforms

If we’re using django-floppyforms we could include logic in our floppyforms/attrs.html template to add specific classes based on a context variable (example). Here’s an example:

{% for name, value in attrs.items %} {{ name }}{% if value != True %}="{{ value }}{% if name == "class" %} {{ extra_classes }}{% endif %}"{% endfor %}

This should work but it’s ugly and in general I do not enjoy maintaining heavy logic in my templates.

Using django-widget-tweaks

I prefer to solve this problem with django-widget-tweaks.

The django-widget-tweaks library provides two solutions to this problem:

  1. add_class template filter
  2. render_field template tag.

The add_class template filter

Mikhail Korobov originally created the django-widget-tweaks library in 2011. It started as a series of template filters for modifying form field attributes from your Django templates.

Here’s an example usage of the add_class filter for adding a CSS class to our form field:

{% load widget_tweaks %}
<p>
    {{ form.username|add_class:"input-lg" }}
    {{ form.username.errors }}
</p>
<p>
    {{ form.password }}
    {{ form.password.errors }}
</p>

I find this solution both easy to read and easy to maintain.

The render_field template tag

I discovered django-widget-tweaks shortly after Mikhail created it. I appreciated his solution for this problem, but I wanted a more HTML-like syntax for my form field customizations. I created the render_field template tag to satisfy that desire.

With the render_field tag you can add attributes to form fields with a much more HTML-like syntax:

{% load widget_tweaks %}
<p>
    {% render_field form.username class+="input-lg" %}
    {{ form.username.errors }}
</p>
<p>
    {% render_field form.password %}
    {{ form.password.errors }}
</p>

As a bonus, with render_field we can also set a CSS class for erroneous and required form fields. See the documentation for more details.

Conclusion

I have not had a chance to use django-floppyforms yet, but I expect that django-widget-tweaks and django-floppyforms would integrate well together.

I am on the lookout for new solutions to this problem, but django-widget-tweaks has served me well so far. I have used it for three years now it remains one of my go-to libraries for new Django projects.

How do you add CSS classes do your Django form fields? If you have another solution please leave a comment below.