Get type of Django form widget from within template

Viewed 25447

I'm iterating through the fields of a form and for certain fields I want a slightly different layout, requiring altered HTML.

To do this accurately, I just need to know the widget type. Its class name or something similar. In standard python, this is easy! field.field.widget.__class__.__name__

Unfortunately, you're not allowed access to underscore variables in templates. Great!

You can test field.field.widget.input_type but this only works for text/password <input ../> types. I need more resolution that that.

To me, however difficult it might look, it makes most sense to do this at template level. I've outsourced the bit of code that handles HTML for fields to a separate template that gets included in the field-loop. This means it is consistent across ModelForms and standard Forms (something that wouldn't be true if I wrote an intermediary Form class).

If you can see a universal approach that doesn't require me to edit 20-odd forms, let me know too!

6 Answers

As of Django 1.11, you can just use widget.input_type. Example:

{% for field in form.visible_fields %}
    <input type="{{ field.field.widget.input_type }}"
           id="{{ field.id_for_label }}"
           name="{{ field.html_name }}"
           placeholder="{{ field.label }}"
           maxlength="{{ field.field.max_length }}" />
{% endfor %}

Perhaps worth pointing out to contemporary readers that django-widget-tweaks provides field_type and widget_type template filters for this purpose, returning the respective class names in lowercase. In the example below I also show the output of the input_type property on the field widget (since Django 1.11), which may also be useful.

forms.py:

class ContactForm(forms.Form):
    name = forms.CharField(
        max_length=150,
        required=True,
        label='Your name'
    )

template.html:

{% load widget_tweaks %}

{% for field in form.visible_fields %}
{{ field.label }}
{{ field.field.widget.input_type }}
{{ field|field_type }}
{{ field|widget_type }})
{% endfor %}

Result:

Your name
text
charfield
textinput

Between these various options you should be able to find the right property to target for just about any use-case. If you need to capture the output of one of these filters for use in if statements, you can use the with template tag.

You can make every view that manages forms inherit from a custom generic view where you load into the context the metadata that you need in the templates. The generic form view should include something like this:

class CustomUpdateView(UpdateView):
    ...
    def get_context_data(self, **kwargs):
       context = super().get_context_data(**kwargs)
       ...
       for f, value in context["form"].fields.items():
          context["form"].fields[f].type = self.model._meta.get_field(f).get_internal_type()
       ...
       return context

In the template you can access these custom properties through field.field:

{% if field.field.type == 'BooleanField' %}
     <div class="custom-control custom-checkbox">
     ...
     </div>
{% endif %}

By using the debugger of PyCharm or Visual Studio Code you can see all the available metadata, if you need something else besides the field type.

Related