Rendering Templates with the Django Template Language
Django's template engine (DTL) renders .html files that mix static markup with {{ variable }} output and {% tag %} logic, evaluated against a context dictionary passed from a view via render(). Variables use dot notation that Django tries as dictionary lookup, then attribute lookup, then list-index lookup, then method call, in that order, stopping at the first that succeeds — so {{ article.author.name }} can traverse a model relationship without any template-side type-checking. By default, all variable output is auto-escaped, converting characters like < and & into HTML entities to prevent cross-site scripting unless explicitly marked safe.
Cricket analogy: It's like a scorecard broadcast graphic that pulls {{ batsman.name }} and auto-formats it safely for the on-screen overlay, trying player-object lookup, then a fallback dictionary, without the broadcast crew hand-coding each name's markup.
Tags and Filters
Template tags like {% if %}, {% for %}, and {% with %} provide control flow, while filters transform a variable's output using pipe syntax, such as {{ price|floatformat:2 }} or {{ name|upper }}, and can be chained: {{ text|truncatewords:30|linebreaks }}. Django ships dozens of built-in filters (date, default, length, slugify, pluralize) covering common formatting needs, and you can register custom filters and tags in a templatetags module inside an app when built-ins don't cover a domain-specific need, such as formatting a currency value per-locale.
Cricket analogy: It's like a scorecard's run-rate filter piping raw ball-by-ball data through a formatting function to display 'CRR: 8.45' instead of a raw unformatted float.
{# templates/articles/list.html #}
{% extends 'base.html' %}
{% load humanize %}
{% block content %}
<h1>Latest Articles</h1>
{% for article in articles %}
<article>
<h2><a href="{% url 'blog:post_detail' post_slug=article.slug %}">{{ article.title }}</a></h2>
<p>{{ article.summary|truncatewords:30 }}</p>
<small>{{ article.created_at|naturaltime }} by {{ article.author.get_full_name|default:'Unknown' }}</small>
</article>
{% empty %}
<p>No articles published yet.</p>
{% endfor %}
{% endblock %}Template Inheritance and Includes
Template inheritance lets a base.html define the overall page skeleton with {% block %} placeholders (e.g., {% block content %}{% endblock %}), and child templates use {% extends 'base.html' %} at the very top of the file to override specific blocks while inheriting everything else, avoiding duplicated <head>, navigation, and footer markup across every page. {% include 'partial.html' %} is the complementary tool for reusable fragments that don't need the parent-child override relationship, such as a comment widget dropped into several unrelated pages, and it can pass extra context with {% include 'card.html' with item=product %}. The TEMPLATES setting's APP_DIRS option controls whether Django also searches each installed app's own templates/ directory, which is how reusable apps ship their own default templates that a project can still override by placing a same-named template earlier in its own TEMPLATES DIRS search path.
Cricket analogy: It's like a domestic league's standard match-day format (base.html) that every franchise (child template) follows, only swapping in team-specific elements like the anthem block, while the toss and innings structure stay inherited.
The {% extends %} tag must be the very first non-comment line in a child template — any tag or output placed before it is a common source of silent template rendering bugs where the extension appears to be ignored.
Never wrap untrusted user input in |safe or mark_safe() just to avoid escaping-related layout issues; doing so disables Django's automatic HTML escaping for that value and opens a direct cross-site scripting vulnerability.
- DTL variables resolve via dict lookup, then attribute, then list-index, then method call, stopping at the first success.
- Output is auto-escaped by default; use |safe or mark_safe() only for trusted, sanitized content.
- Tags ({% if %}, {% for %}, {% with %}) provide control flow; filters transform output and can be chained with |.
- Custom filters/tags live in an app's templatetags module and are loaded with {% load %}.
- {% extends %} plus {% block %} implements template inheritance; it must be the first line in the child template.
- {% include %} inserts reusable fragments and can pass extra context via the with clause.
- APP_DIRS and TEMPLATES DIRS settings control where Django searches for templates, including app-shipped defaults.
Practice what you learned
1. In what order does Django try to resolve {{ article.author }}?
2. What does Django do by default when rendering a variable containing '<script>'?
3. Where must the {% extends %} tag appear in a child template?
4. What is the main difference between {% include %} and {% extends %}?
Was this page helpful?
You May Also Like
Context and Template Tags
Understand how Django builds the template context (including automatic context processors) and how to write custom template tags and filters for reusable presentation logic.
Function-Based Views
Learn how Django's function-based views (FBVs) take a request and return a response using plain Python functions, and when they're the right choice over class-based views.
Class-Based Views
Understand how Django's class-based views (CBVs) organize request handling into methods on a class, and how generic CBVs and mixins reduce boilerplate for common patterns like CRUD.
Related Reading
Related Study Notes in Web Development
Browse all study notesWebSockets Study Notes
Web Development · 30 topics
Web DevelopmentWebAssembly Study Notes
WebAssembly · 30 topics
Web DevelopmentgRPC Study Notes
Protocol Buffers · 30 topics
Web DevelopmentSpring Boot Study Notes
Java · 30 topics
Web DevelopmentFlask Study Notes
Python · 30 topics
Web DevelopmentNext.js Study Notes
JavaScript · 30 topics