Django: Showing the help_text of a model field as a title attribute in the form

Maybe it’s just me, but I find the standard way Django outputs the help_text on form elements quite annoying. If you use form.as_ul, it looks something like this:

<li><label>Label text:</label><input type="text">This is the helptext.</li>

Which is great, except it’s really difficult to make it look nice. What I would rather have, is help text only appearing on the field the user is busy with. This can be achieved by putting the help text as a title to the input element, and optionally combining this with a jQuery tooltip library of your choice.

Now, to achieve this isn’t all trivial. Let’s start with an example Model definition.

class Person(models.Model):  
    name = models.CharField(max_length=50, help_text="Your first name.")   
    surname = models.CharField(max_length=50, help_text="Your last name.")  

When rendering the form for this model, we want the help_text to appear as a title attribute, rather than at the end of the string. However, since we know our Person Model can change at any time, and we don’t want to update the Form every time, we need a way to do this as generally as possible, without referencing each field on its own. This can be achieved by editing the widgets attribute of the Meta class of the Form, like so:

class PersonForm(ModelForm):
    class Meta:
        model = Person
        widgets = {}
        fields = model._meta.get_all_field_names()
        for field in fields:
            f = Profile._meta.get_field(field)
            formField = f.formfield()
            if formField: 
                widgets[field] = type(formField.widget)(attrs={'title': f.help_text})

Those last few lines are where the magic happens. First, we get all the field names of the Person object with the _meta.get_all_field_names method. Then we loop through these names, getting the associated FormField for each Model field. If that exists, we update the widgetsdictionary to contain a newly defined widget of the appropriate type, as well as the title attribute we wanted.

The last step is to display all this in a template. We can’t use the form.as_ul method directly, because that will give us the help text as plain text again. We have to use a customized form, like this:

 {% for field in form %}
    <li>
        {{ field.label_tag }}{{ field }}
    </li>
{% endfor %}

And there you have it. Beautiful Django code that doesn’t violate the DRY principle, and displays the help_text as a title in the form.

Fork Herman Schaaf's IronZebra blog on GitHub

Herman Schaaf is a full-stack developer in Tokyo, Japan. He often works on language-related open source projects, and has recently developed a keen interest in Go. He's a friendly guy who sometimes talks about himself in third person. You can reach him at @ironzeb or via email. Or, read some more posts.