[Django]-Get type of Django form widget from within template

27👍

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

45👍

Making a template tag might work? Something like field.field.widget|widget_type

Edit from Oli: Good point! I just wrote a filter:

from django import template
register = template.Library()

@register.filter('klass')
def klass(ob):
    return ob.__class__.__name__

And now {{ object|klass }} renders correctly. Now I’ve just got to figure out how to use that inside a template’s if statement.

Edit from Oli #2: I needed to use the result of that in an if statetement in-template, so I just shifted all that logic into the templatetag. Magic. Thanks for poking me in the right direction.

👤rinti

37👍

Following up on the accepted answer – the enhanced if tag in Django 1.2 allows you to use filters in if tag comparisons. So you could now do your custom html/logic in the template like so:

<ul>
{% for field in form.fields %}
  <li>
    {% if field.field.widget|klass == "Textarea" %}
    <!-- do something special for Textarea -->
    <h2>Text Areas are Special </h2>
    {% else %}      
      {{ field.errors }}
      {{ field.label_tag }}
      {{ field }}
    {% endif %}

  </li>
{% endfor %}
</ul>

21👍

Following the answer from Oli and rinti: I used this one and I think it is a bit simpler:

template code: {{ field|fieldtype }}

filter code:

from django import template
register = template.Library()

@register.filter('fieldtype')
def fieldtype(field):
    return field.field.widget.__class__.__name__

1👍

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.

0👍

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.

0👍

For anyone here whose purpose is to customise widget style according to its type, based on @oli answer great idea, I decided to set the mapping directly in the template filter, and return the corresponding classes directly. This avoids messing with {% if %} statements in the template.

from django import template

register = template.Library()

BASIC_INPUT = "border border-indigo-300 px-2.5 py-1.5 rounded-md focus:outline-none"

mapping = {
    "Select": BASIC_INPUT,
    "TextInput": BASIC_INPUT,
    "EmailInput": BASIC_INPUT,
    "RegionalPhoneNumberWidget": BASIC_INPUT,
    "ModelSelect2": "",  # let the default markup
}


@register.filter("get_field_classes")
def get_field_classes(field):
    widget_class_name = field.field.widget.__class__.__name__
    try:
        return mapping[widget_class_name]
    except KeyError:
        raise ValueError(f"Classes related to {widget_class_name} are not defined yet")

Then in your default form template (e.g: django/forms/default.html), when browsing fields, with the help of django-widget-tweaks package:

{% load widget_tweaks %}
{% load get_field_classes %}

{% for field in form.visible_fields %}

  {# .... #}

  <div>
    {% with field_classes=field|get_field_classes %}
        {{ field|add_class:field_classes }}
    {% endwith %}
  </div>
{% endfor %}

Leave a comment