Django Templates
Django’s template layer provides a lightweight template syntax for dynamically rendering backend data into HTML pages. This article covers template variables, filters, tags (for/if/with), custom filters and inclusion tags, and how to use template inheritance and imports.
Template Syntax
Django templates use two kinds of delimiters:
{{ }}— for variables and expressions{% %}— for logic (loops, conditions, etc.)
# views.py — pass various Python types to the template
def index(request):
n = 123
f = 11.11
s = 'hello world'
b = True
l = ['Alice', 'Bob', 'Carol', 'Dave']
t = (111, 222, 333, 444)
d = {'username': 'jason', 'age': 18, 'info': 'interesting person'}
se = {'a', 'b', 'c'}
def func():
print('func was called')
return 'Your other half is waiting'
class MyClass(object):
def get_self(self):
return 'self'
@staticmethod
def get_func():
return 'func'
@classmethod
def get_class(cls):
return 'cls'
# When an object is displayed in the template it triggers __str__, just like print()
def __str__(self):
return 'MyClass instance'
obj = MyClass()
# Pass all local variables to the template at once
return render(request, 'index.html', locals())In the template:
<p>{{ n }}</p>
<p>{{ f }}</p>
<p>{{ s }}</p>
<p>{{ b }}</p>
<p>{{ l }}</p>
<p>{{ d }}</p>
<p>{{ t }}</p>
<p>{{ se }}</p>
<!-- Passing a function name: Django automatically calls it (no arguments supported) -->
<p>{{ func }}</p>
<!-- Passing a class name: Django automatically instantiates it -->
<p>{{ MyClass }}</p>
<!-- The template engine auto-detects whether a variable is callable and calls it if so -->
<p>{{ obj }}</p>
<p>{{ obj.get_self }}</p>
<p>{{ obj.get_func }}</p>
<p>{{ obj.get_class }}</p>Dot Notation
Django templates use the dot (.) for all attribute and index access:
<!-- Dict key access -->
<p>{{ d.username }}</p>
<!-- List index access -->
<p>{{ l.0 }}</p>
<!-- Nested access — mix keys and indexes freely -->
<p>{{ d.hobby.3.info }}</p>The dot notation works for dict keys, list/tuple indexes, object attributes, and object methods — Django resolves them in that order automatically.
Filters
Filters are built-in template methods that transform a variable’s value before display. Django ships with over 60 filters; each filter accepts at most two parameters.
Syntax: {{ data|filter_name:parameter }}
Common Built-in Filters
<!-- Length of a string or list -->
<p>{{ s|length }}</p>
<!-- Default value — if the variable is falsy, show the fallback -->
<p>{{ b|default:'nothing here' }}</p>
<!-- File size — convert bytes to the largest appropriate unit -->
<p>{{ file_size|filesizeformat }}</p>
<!-- Date formatting -->
<p>{{ current_time|date:'Y-m-d H:i:s' }}</p>
<!-- Slice (supports step) -->
<p>{{ l|slice:'0:4:2' }}</p>
<!-- Truncate characters (appends '...') -->
<p>{{ info|truncatechars:9 }}</p>
<!-- Truncate words (splits on spaces, no '...') -->
<p>{{ egl|truncatewords:9 }}</p>
<!-- Remove a specific character -->
<p>{{ msg|cut:' ' }}</p>
<!-- Join list elements with a separator -->
<p>{{ l|join:'$' }}</p>
<!-- Add a number -->
<p>{{ n|add:10 }}</p>
<!-- Concatenate strings -->
<p>{{ s|add:msg }}</p>
<!-- Mark a string as safe HTML (disable auto-escaping) -->
<p>{{ hhh|safe }}</p>Marking Strings Safe on the Backend
from django.utils.safestring import mark_safe
res = mark_safe('<h1>Safe HTML</h1>')
# Pass res to the template — it will render as HTML, not escaped textThis is useful when you build HTML strings in the view and want to render them without escaping in the template.
Tags
for Loop
{% for item in l %}
<p>{{ forloop }}</p> <!-- forloop context variable -->
<p>{{ item }}</p>
{% endfor %}
<!--
forloop provides these keys:
counter0 — 0-based iteration count
counter — 1-based iteration count
revcounter — remaining iterations (1-based)
revcounter0 — remaining iterations (0-based)
first — True on the first iteration
last — True on the last iteration
parentloop — reference to the outer loop (for nested loops)
-->if / elif / else
{% if b %}
<p>condition is true</p>
{% elif s %}
<p>fallback condition</p>
{% else %}
<p>default</p>
{% endif %}Combining for and if
{% for item in lll %}
{% if forloop.first %}
<p>First item</p>
{% elif forloop.last %}
<p>Last item</p>
{% else %}
<p>{{ item }}</p>
{% endif %}
{% empty %}
<p>The iterable is empty — nothing to loop over.</p>
{% endfor %}Iterating Over a Dictionary
{% for key in d.keys %}
<p>{{ key }}</p>
{% endfor %}
{% for value in d.values %}
<p>{{ value }}</p>
{% endfor %}
{% for key, value in d.items %}
<p>{{ key }}: {{ value }}</p>
{% endfor %}with — Creating Aliases
with lets you alias a deeply nested expression to a short name, valid only within the block:
{% with d.hobby.3.info as nb %}
<p>{{ nb }}</p>
<!-- The original long expression also works inside the block -->
<p>{{ d.hobby.3.info }}</p>
{% endwith %}Custom Filters, Tags, and inclusion_tag
Setup (Three Required Steps)
- Create a folder named exactly
templatetagsinside your app directory. - Create a Python file with any name inside it, e.g.
mytag.py. - Start the file with exactly these two lines:
from django import template
register = template.Library()Custom Filter
A custom filter can accept at most two arguments (the variable and one extra parameter):
# mytag.py
@register.filter(name='baby')
def my_sum(v1, v2):
return v1 + v2Usage in template:
{% load mytag %}
<p>{{ n|baby:666 }}</p>Custom Tag (simple_tag)
A custom simple tag works like a function and can accept any number of arguments:
@register.simple_tag(name='plus')
def index(a, b, c, d):
return '%s-%s-%s-%s' % (a, b, c, d)Usage in template (arguments separated by spaces):
{% load mytag %}
<p>{% plus 'jason' 123 123 123 %}</p>Custom inclusion_tag
An inclusion_tag renders a partial template and inserts the result at the call site — useful for reusable UI components:
@register.inclusion_tag('left_menu.html')
def left(n):
data = ['Item {}'.format(i) for i in range(n)]
return locals() # passes 'data' to left_menu.htmlUsage:
{% load mytag %}
{% left 5 %}Template Inheritance
Template inheritance lets you define a base layout and override specific sections in child templates — ideal for sites where every page shares the same header, footer, and navigation.
Base Template (home.html)
Mark the sections that child templates are allowed to override using {% block %}:
<!DOCTYPE html>
<html>
<head>
{% block css %}{% endblock %}
</head>
<body>
<nav><!-- shared navigation --></nav>
{% block content %}
<!-- default content shown when no child overrides this block -->
{% endblock %}
{% block js %}{% endblock %}
</body>
</html>Child Template
{% extends 'home.html' %}
{% block css %}
<style>
/* page-specific styles */
</style>
{% endblock %}
{% block content %}
<h1>My Page Content</h1>
<p>This replaces the parent block.</p>
{% endblock %}
{% block js %}
<script>
// page-specific scripts
</script>
{% endblock %}A child template that extends a parent inherits its entire layout. Only blocks that are explicitly redefined will be replaced.
Template Import (include)
{% include %} inserts a reusable partial template at the point where it is called — no inheritance involved:
{% include 'sidebar.html' %}Use include when you have a small UI fragment (e.g. a sidebar, a card component, a form widget) that you want to reuse across pages without setting up a full parent-child inheritance relationship.