What's the best way to handle valueless attributes in Django templates?

I'm looking for a cleaner way to conditionally add valueless HTML attributes, like disabled, in a Django template.

For example, in the code snippet below, the {% if %} tag is embedded directly inside the <button> tag, which I find makes the code harder to read.

<button type="submit"
    id="prev-page-btn"
    class="btn btn-secondary w-50"
    name="prev-page-btn"
    {% if not page_obj.has_previous %}disabled{% endif %}>
   Previous
</button>

Is there a more elegant solution for this? I'd like to avoid duplicating the entire <button> block just to control the presence of the disabled attribute.

Instead of putting the {% if %} logic directly in the tag, assign it to a context variable or use the {% with %} tag.

Like:

{% with 'disabled' if not page_obj.has_previous else '' as disabled_attr %}
<button type="submit"
    id="prev-page-btn"
    class="btn btn-secondary w-50"
    name="prev-page-btn"
    {{ disabled_attr }}>
   Previous
</button>
{% endwith %}

What might help here is a custom template tag [Django-doc], like:

# some_app/templatetags/htmlattr.py

from django.forms.utils import flatattr

from django import template

register = template.Library()


@register.simple_tag(name='html_attributes')
def html_attributes(**kwargs):
    return flatatt(kwargs)

then we can load this template tag in the template, and use it accordingly:

{% load htmlattr %}

{% with not page_obj.has_previous as no_previous %}

<button type="submit"
    id="prev-page-btn"
    class="btn btn-secondary w-50"
    name="prev-page-btn"
    {% html_attributes disabled=no_previous %}>
   Previous
</button>
{% endwith %}

Our html_attributes allows to work with key-values. If the value is False or None, it will not be included. If the value is a non-boolean, it will be passed as the value of the HTML attribute, so:

<tag {% html_attributes foo='bar' qux=False bla=True %}>

will be encoded in HTML as:

<tag foo="bar" bla>

We thus can replace the above with:

{% with not page_obj.has_previous as no_previous %}

<button {% html_attributes type='submit' id='prev-page-btn' class='btn btn-secondary w-50' name='prev-page-btn' disabled=no_previous %}>
   Previous
</button>
{% endwith %}

Django uses this the flatatt internally to format HTML attributes for wigets of a form.

Вернуться на верх