Using Variable Comparison as Django Template Tag Argument

The Problem

Let's say you wanted to have a boolean argument to your product_list_item Django tag and on a certain page, you wanted to pass True for the first four products. Doing something like the code below will result in an error.


<ul class="product-list">
{% for product in products %}
    {% product_list_item product forloop.counter0 <= 4 %}
{% endfor %}
</ul>

Simple Comparison Django Template Tags To The Rescue

It's not pretty, but a simple workaround is to make a comparison template tag library and use the results of those tags to pass into the template tag you're trying to pass a comparison into.


@register.simple_tag(takes_context=True)
def eq(context, x, y, var='result'):
    context[var] = x == y
    return ''


@register.simple_tag(takes_context=True)
def lt(context, x, y, var='result'):
    context[var] = x < y
    return ''


@register.simple_tag(takes_context=True)
def lte(context, x, y, var='result'):
    context[var] = x <= y
    return ''


@register.simple_tag(takes_context=True)
def gte(context, x, y, var='result'):
    context[var] = x >= y
    return ''


@register.simple_tag(takes_context=True)
def gt(context, x, y, var='result'):
    context[var] = x > y
    return ''

To use these in the above example would look like this:


{% load comparison %}
<ul class="product-list">
{% for product in products %}
    {% lte forloop.counter0 4 %}
    {% product_list_item product result %}
{% endfor %}
</ul>

Django wants you to keep business logic out of templates as much as possible, but sometimes it's a little too restrictive. Also, full Django template tags allow you to parse and execute the arguments however you wish. So automatically parsing and executing parts of the argument list could get annoying with full tags.