Use built-in Django tags on Django templates

Problem

You want to do elaborate operations on Django templates, such as: Logical operations to present 'x' or 'y' content, loop over data to create layout lists, add template comments, among other things.

Solution

Use one of Django's many built-in tags. Django offers built-in tags for several purposes that are available by default on all Django templates.

How it works

Django offers several built-in tags that offer immediate access to elaborate operations on Django templates. I'll classify each of these built-in tags into functional sections so it's easier to identify them. The functional classes I'll use are: Dates, Forms, Logical operations, Loops, Python code, Spacing and special characters, Template structures, Development and testing & Urls.

Dates

Note {% now %} tag also accepts predefined date variables

The {% now %} tag can also accept predefined date variables. For example, {% now "DATE_FORMAT" %}, {% now "DATETIME_FORMAT" %}, {% now "SHORT_DATE_FORMAT" %} or {% now "SHORT_DATETIME_FORMAT"}.

The date variables in themselves are also composed of date strings. For example DATE_FORMAT default's to "N j, Y" (e.g. Jan 1, 2015), DATETIME_FORMAT defaults to "N j, Y, P" (e.g. Jan 1, 2015, 12 a.m.), SHORT_DATE_FORMAT defaults to "m/d/Y" (e.g. 01/01/2015) and SHORT_DATETIME_FORMAT defaults to "m/d/Y P" (e.g. 01/01/2015 12 a.m.). Each date variable can be overridden with different date strings in a project's settings.py file.

Forms

Logical operations

Listing 1 - Django {% if %} tag with {% elif %} and {% else %}

{% if drinks %}             {% if drinks %}              {% if drinks %}
  We have drinks!                We have drinks              We have drinks 
{% endif %}                 {% else %}                   {% elif drinks_on_sale %}
                                No drinks,sorry              We have drinks on sale!
                            {% endif %}                  {% else %}
                      	                                     No drinks, sorry 
                                                         {% endif %}

*Note a variable must both exist and not be empty to match a condition
 a variable that just exists and is empty does not match. 
Listing 2 - Django {% firstof %} tag and equivalent {% if %}{% elif %}{% else %} tags

# Firstof example
{% firstof var1 var2 var3 %} 

# Equivalent of firstof example
{% if var1 %}
    {{var1|safe}}
{% elif var2 %}
    {{var2|safe}}
{% elif var3 %}
    {{var3|safe}}
{% endif %}

# Firstof example with a default value in case of no match (i.e, all variables are empty) 
{% firstof var1 var2 var3 "All vars are empty" %} 

*Note the |safe syntax after each variable is a Django filter
 Django filters are described in the next recipe. 

# Assign the firstof result to another variable 
{% firstof var1 var2 var3 as resultof %}
# resultof variable now accesible as {{resultof}}

Loops

Listing 3 - Django {% for %} tag and {% for %} with {% empty %}

<ul>                                     <ul>
{% for drink in drinks %}                 {% for storeid,store in stores %}
 <li>{{ drink.name }}</li>                 <li><a href="/stores/{{storeid}}/">{{store.name}}</a></li>
{% empty %}                               {% endfor %}
 <li>No drinks, sorry</li>               </ul>
{% endfor %}
</ul>
Table 1 - Django {% for %} tag variables
VariableDescription
forloop.counterThe current iteration of the loop (1-indexed)
forloop.counter0The current iteration of the loop (0-indexed)
forloop.revcounterThe number of iterations from the end of the loop (1-indexed)
forloop.revcounter0The number of iterations from the end of the loop (0-indexed)
forloop.firstTrue if it's the first time through the loop
forloop.lastTrue if it's the last time through the loop
forloop.parentloopFor nested loops, this is the parent loop the current one
Listing 4 - Django {% for %} tag and {% regroup %}

# Dictionary definition
stores = [
    {'name': 'Downtown', 'street': '385 Main Street', 'city': 'San Diego'},
    {'name': 'Uptown', 'street': '231 Highland Avenue', 'city': 'San Diego'},
    {'name': 'Midtown', 'street': '85 Balboa Street', 'city': 'San Diego'},
    {'name': 'Downtown', 'street': '639 Spring Street', 'city': 'Los Angeles'},
    {'name': 'Midtown', 'street': '1407 Broadway Street', 'city': 'Los Angeles'},
    {'name': 'Downton', 'street': '50 1st Street', 'city': 'San Francisco'},
]

# Template definition with regroup and for tags 
{% regroup stores by city as city_list %}

<ul>
{% for city in city_list %}
    <li>{{ city.grouper }}
    <ul>
        {% for item in city.list %}
          <li>{{ item.name }}: {{ item.street }}</li>
        {% endfor %}
    </ul>
    </li>
{% endfor %}
</ul>

# Output
San Diego
    Downtown : 385 Main Street
    Uptown : 231 Highland Avenue
    Midtown : 85 Balboa Street
Los Angeles
    Downtown: 639 Spring Street
    Midtown: 1407 Broadway Street
San Francisco
    Downtown: 50 1st Street

Python code

Note Inline Python code only allowed behind the scenes in custom Django tags or filters

Django templates don't allow the inclusion of inline Python code. In fact, the closest thing Django templates allow to inline Python code is through the {% with %} tag which isn't very sophisticated.

The only way to make custom Python code work on Django templates is to embed the code inside a custom Django tag or filter. This way you can place a custom Django tag or filter on a template and the Python code runs behind the scenes.

However, before attempting to create a custom Django tag or filter, I suggest you carefully analyze the functionality you wish the custom Python code to do. Nearly all template functionality can be solved using the built-in Django tags and filters. So I advise you to look closely for a solution using the built-in Django tags described in this recipe or the built-in Django filters described in the next recipe.

Spacing and special characters

Template structures

Note Register custom Django tags and filters with the builtins option if you use {% load %} too much

If you find yourself using the {% load %} tag on many templates, you may find it easier to register Django tags and filters with the builtins option so they become accesible on all templates without the need to use {% load %}. See the recipe Customize Django template configuration for more details on how to use the builtins option.

Development and testing

Urls