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