CodeMIRAI
0 XP

The template language

3 min read 5 runnable examples Quiz included

Django's template language is deliberately small. It displays data and makes simple decisions; real logic belongs in Python, in your views.

Every example starts with the same few lines of setup, which a project's settings normally provide.

Filters

A filter changes a value before it's shown, written after a |:

Python
import django
from django.conf import settings

settings.configure(TEMPLATES=[{"BACKEND": "django.template.backends.django.DjangoTemplates"}])
django.setup()

import datetime
from django.template import Context, Template

t = Template("""{{ name|title }}
{{ name|upper }} has {{ courses|length }} course{{ courses|length|pluralize }}: {{ courses|join:", " }}
Joined {{ joined|date:"j F Y" }}
Price: {{ price|floatformat:2 }}
Bio: {{ bio|default:"No bio yet" }}
{{ summary|truncatewords:6 }}""")

print(t.render(Context({
    "name": "asha verma",
    "courses": ["Python", "SQL"],
    "joined": datetime.date(2024, 7, 1),
    "price": 499,
    "bio": "",
    "summary": "A practical course that takes you from zero to building real programs.",
})))
FilterDoes
title, upper, lower, capfirstchange capitals
lengththe number of items, or characters
pluralizeadds "s" unless the value is 1
join:", "joins a list with a separator
date:"j F Y"formats a date
floatformat:2rounds to 2 decimal places
default:"..."a fallback for empty or false values
truncatewords:6cuts text to 6 words, adding "…"

Filters chain from left to right: {{ courses|length|pluralize }}.

if

Python
import django
from django.conf import settings

settings.configure(TEMPLATES=[{"BACKEND": "django.template.backends.django.DjangoTemplates"}])
django.setup()

from django.template import Context, Template

t = Template("""{% if score >= 90 %}Excellent{% elif score >= 60 %}Passed{% else %}Try again{% endif %}
{% if user and not user.banned %}Welcome, {{ user.name }}{% endif %}
{% if "SQL" in courses %}You're taking SQL{% endif %}""")

for score in [95, 72, 40]:
    print(t.render(Context({"score": score, "user": {"name": "Leo", "banned": False}, "courses": ["SQL"]})))
    print("--")

{% if %} supports elif, else, and, or, not, in and the comparison operators.

for

Python
import django
from django.conf import settings

settings.configure(TEMPLATES=[{"BACKEND": "django.template.backends.django.DjangoTemplates"}])
django.setup()

from django.template import Context, Template

t = Template("""{% for lesson in lessons %}{{ forloop.counter }}. {{ lesson.title }}{% if lesson.done %} (done){% endif %}{% if not forloop.last %}, {% endif %}{% empty %}No lessons yet.{% endfor %}""")

print(t.render(Context({"lessons": [{"title": "Variables", "done": True}, {"title": "Loops", "done": False}, {"title": "Functions", "done": False}]})))
print(t.render(Context({"lessons": []})))
Inside a for loopMeans
forloop.counter1, 2, 3...
forloop.counter00, 1, 2...
forloop.first, forloop.lasttrue on the first or last pass
{% empty %}what to show when the list is empty

with and comments

Python
import django
from django.conf import settings

settings.configure(TEMPLATES=[{"BACKEND": "django.template.backends.django.DjangoTemplates"}])
django.setup()

from django.template import Context, Template

t = Template("""{# a one-line comment: never sent to the browser #}
{% with total=items|length %}{{ total }} item{{ total|pluralize }} in your basket{% endwith %}
{% comment %}A longer comment,
over several lines.{% endcomment %}""")
print(t.render(Context({"items": ["pen", "notebook", "bag"]})).strip())

{% with %} names a value for a block of template, which avoids repeating a long expression.

Automatic escaping

Python
import django
from django.conf import settings

settings.configure(TEMPLATES=[{"BACKEND": "django.template.backends.django.DjangoTemplates"}])
django.setup()

from django.template import Context, Template

comment = '<script>alert("hacked")</script> Great course!'
print(Template("{{ comment }}").render(Context({"comment": comment})))
print(Template("{{ comment|safe }}").render(Context({"comment": comment})))

Django escapes every variable by default: <, >, & and quotes become entities, so text from users is shown rather than run. This blocks cross-site scripting without any effort on your part. |safe turns escaping off for one value: use it only for HTML you produced and trust, never for user input.

Keep logic in the view

Templates can't call functions with arguments, do arithmetic beyond simple filters, or run queries with conditions, and that's on purpose. Prepare the data in the view:

Python
def dashboard(request):
    courses = request.user.courses.filter(active=True)
    context = {
        "courses": courses,
        "completed": courses.filter(progress=100).count(),
    }
    return render(request, "progress/dashboard.html", context)

The template then only displays {{ completed }}. This keeps templates readable for designers, and logic testable in Python.

Finished this lesson?Mark it complete to earn +20 XP and move your progress on.