The Django Template Language (DTL)
1. Why Templates?
Hard-coding HTML inside a Python string (as we did in the last chapter) is a maintenance nightmare. Templates solve this by letting you write real HTML files with special Django tags that get replaced with dynamic data at runtime.
2. Setting Up the Templates Directory
Create a templates/ folder at the root of your project, then tell Django where to find it in settings.py:
# mysite/settings.py
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
# Add BASE_DIR / 'templates' so Django finds your HTML files:
'DIRS': [BASE_DIR / 'templates'],
'APP_DIRS': True, # Also looks inside each app's templates/ subfolder
'OPTIONS': { 'context_processors': [...] },
},
]
Your directory structure should now look like this:
myproject/
├── templates/ ← Global templates folder
│ └── blog/
│ ├── base.html
│ └── post_list.html
├── blog/
└── mysite/
3. Your First Template and Context
A context is a Python dictionary you pass from a view to a template. Every key in the dictionary becomes a variable available inside the template. Create templates/blog/post_list.html:
<!-- templates/blog/post_list.html -->
<!DOCTYPE html>
<html lang="en">
<head><title>My Blog</title></head>
<body>
<h1>{{ page_title }}</h1>
<!-- Loop through the posts list passed from the view -->
{% for post in posts %}
<article>
<h2>{{ post.title }}</h2>
<p>Published: {{ post.created_at|date:"F j, Y" }}</p>
<p>{{ post.body|truncatewords:30 }}</p>
</article>
{% empty %}
<p>No posts yet. Check back soon!</p>
{% endfor %}
</body>
</html>
Now update the view to render this template using render():
# blog/views.py
from django.shortcuts import render
def post_list(request):
# This is the context dictionary — keys become template variables.
context = {
'page_title': 'Welcome to My Blog',
'posts': [], # We'll replace this with real DB data in Chapter 10
}
# render() loads the template, injects the context, and returns an HttpResponse.
return render(request, 'blog/post_list.html', context)
4. DTL Syntax Reference
| Syntax | Purpose | Example |
|---|---|---|
{{ variable }} | Output a variable’s value | {{ user.username }} |
{{ variable|filter }} | Transform output with a filter | {{ title|upper }} |
{% tag %} | Logic and control flow | {% if user.is_authenticated %} |
{# comment #} | Template comment (not rendered) | {# TODO: fix this #} |
5. Template Inheritance — The Most Powerful DTL Feature
Template inheritance lets you define a single base template with common HTML (header, navigation, footer) and have every other template extend it. This eliminates code duplication entirely.
Step 1: Create templates/blog/base.html — the parent skeleton:
<!-- templates/blog/base.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}My Blog{% endblock %}</title>
<link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
<nav><a href="/">Home</a></nav>
<main>
<!-- Child templates fill in this block -->
{% block content %}{% endblock %}
</main>
<footer><p>© 2024 My Blog</p></footer>
</body>
</html>
Step 2: Update post_list.html to extend the base:
<!-- templates/blog/post_list.html -->
<!-- 1. Declare which template this extends -->
{% extends "blog/base.html" %}
<!-- 2. Override the title block -->
{% block title %}All Posts — My Blog{% endblock %}
<!-- 3. Fill in the content block -->
{% block content %}
<h1>{{ page_title }}</h1>
{% for post in posts %}
<h2>{{ post.title }}</h2>
{% empty %}
<p>No posts yet.</p>
{% endfor %}
{% endblock %}
When Django renders post_list.html, it merges it into base.html, placing the child’s block content exactly where the parent’s {% block %} tags are defined. All the shared navigation and footer come automatically from the base — you only write them once.