The Template Engine
When building a web application, it is best to keep your Python logic separate from your HTML presentation. Writing HTML directly inside your Django views makes the code hard to read and difficult to maintain. Django solves this using templates. A template is a text file that defines the structure of a web page while allowing you to insert dynamic data from your database.
Configuration
Django configures templates in your settings.py file inside the TEMPLATES list. By default, Django uses its own built-in template engine.
The two most important settings in this configuration are DIRS and APP_DIRS. The DIRS setting tells Django where to look for templates outside of your specific applications, such as a project-wide templates folder. Setting APP_DIRS to True tells Django to look inside each installed application for a templates folder automatically.
import os
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
# Point to a central templates folder in your project root
"DIRS": [BASE_DIR / "templates"],
# Allow Django to search inside each app's 'templates' folder
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]Folder Structure and Namespacing
When APP_DIRS is active, you might assume you can just drop an index.html file directly into your application's templates folder. However, Django searches for templates across all applications and returns the first match it finds. If two different applications both have an index.html file, Django might load the wrong one.
To prevent this, you should always create a subfolder inside the templates directory with the exact same name as your application. You then place your HTML files inside that subfolder.
my_project/
blog/
templates/
blog/
index.html
post_detail.html
store/
templates/
store/
index.html
product_detail.htmlThis folder structure creates a namespace. When you want to load a template from your view, you can explicitly specify the folder name, guaranteeing Django grabs the correct file.
from django.shortcuts import render
def blog_index(request):
# Django will look for 'blog/index.html' specifically, ignoring 'store/index.html'
return render(request, "blog/index.html")Template Fundamentals
The Django template language uses two main syntaxes to insert dynamic content into your HTML:
- Variables output raw data passed down from your view.
- Template tags provide structural logic like loops, conditional statements, and formatting operations.
<!-- Variables use double curly braces to output data -->
<h1>Welcome back, {{ user.first_name }}</h1>
<!-- Tags use curly braces and percent signs for logic -->
{% if unread_messages %}
<p>You have new messages.</p>
{% else %}
<p>Your inbox is empty.</p>
{% endif %}
<!-- Tags also handle looping through data -->
{% for item in shopping_cart %}
<p>{{ item.name }} - ${{ item.price }}</p>
{% endfor %}Passing Data with Context
When you want to display dynamic data in your HTML, you need a way to send that data from your Python view to your template file. Django handles this using a Context.
A context is essentially a dictionary that maps variable names to actual Python objects. When the template engine reads your HTML and sees a variable tag like , it looks inside the context dictionary to find the matching value.
In a standard Django view, you do not need to create a formal Context object yourself. You simply pass a standard Python dictionary to the render() shortcut function, and Django handles the conversion behind the scenes.
Here is an example of passing a context dictionary from a view:
from django.shortcuts import render
def profile_view(request):
# This dictionary is your context
context_data = {
"username": "Alice",
"account_type": "Premium",
"messages": 5
}
return render(request, "profile.html", context_data)Inside your profile.html template, you can now access those dictionary keys directly as variables:
<h1>Welcome back, {{ username }}!</h1>
<p>Your account level is: {{ account_type }}</p>
<p>You have {{ messages }} unread messages.</p>The template engine also uses the context to manage temporary variables. For example, when you use a {% for %} loop, Django temporarily adds the loop variables into the context and removes them once the loop finishes so they do not interfere with the rest of your page.
Comments
Just like regular Python code, Django templates allow you to add comments. These comments are strictly for developers and are entirely removed by the template engine before the final HTML is sent to the user's browser. This makes them much safer than standard HTML comments, which anyone can see by viewing the page source.
For a quick single-line comment, wrap your text in {# and #}.
{# This text will not appear in the final HTML #}
<h1>Welcome to our site</h1>
<!-- Standard HTML comments WILL appear in the browser source code -->If you need to write a longer explanation or temporarily disable a large chunk of code, use the {% comment %} tag instead.
<p>This is visible to the user.</p>
{% comment %}
Everything inside this block is completely ignored by Django.
It is very useful for explaining complex template logic.
<div class="test-layout">
<p>Even HTML inside here will not be rendered.</p>
</div>
{% endcomment %}Template Inheritance
The most powerful feature of Django's template engine is template inheritance. It allows you to build a base skeleton template that contains all the common elements of your site, like the navigation bar and footer. You can then define specific blocks inside this base template that child templates can override.
Here is an example of a base template named base.html. It defines a title block and a content block.
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}My Default Title{% endblock %}</title>
</head>
<body>
<nav>
<a href="/">Home</a>
<a href="/about/">About</a>
</nav>
<main>
<!-- Child templates will inject their specific content here -->
{% block content %}
{% endblock %}
</main>
<footer>Copyright 2026</footer>
</body>
</html>Now you can create a child template that inherits from base.html using the {% extends %} tag. The child template only needs to define the specific blocks it wants to change.
{% extends "base.html" %}
{% block title %}About Us{% endblock %}
{% block content %}
<h1>About Our Company</h1>
<p>We build great Django applications.</p>
{% endblock %}When Django renders the child template, it reads the base template and replaces the empty blocks with the content provided by the child. This keeps your code completely dry and makes site-wide design changes incredibly easy because you only have to update the base file.
