CodeMIRAI
0 XP

Templates

3 min read 3 runnable examples Quiz included

Building HTML with strings in a view gets messy fast. Templates are HTML files with placeholders that Django fills in with data.

Rendering a template

This example runs right here. Its first lines configure Django, the job a project's settings.py normally does:

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

page = Template("<h1>{{ title }}</h1><p>{{ count }} lessons, taught by {{ teacher.name }}.</p>")
print(page.render(Context({"title": "Python", "count": 64, "teacher": {"name": "Mira"}})))
  • {{ title }} is replaced by the value of title in the context, the dictionary of data passed in.
  • {{ teacher.name }} looks inside: a dictionary key, an attribute or a method, whichever exists.

Templates in a project

In a real project, templates are files. By convention each app keeps them in templates/<app name>/:

Output
courses/
    templates/
        courses/
            course_list.html
            course_detail.html

The repeated courses folder looks odd, but it's deliberate: Django searches every app's templates folder, so the extra level stops two apps' detail.html files from clashing.

A view renders a template with render:

Python
from django.shortcuts import render

from .models import Course


def course_list(request):
    courses = Course.objects.order_by("title")
    return render(request, "courses/course_list.html", {"courses": courses})

render(request, template_name, context) loads the template, fills it with the context, and returns an HttpResponse.

Sharing a layout: extends and block

Every page of a site shares a header, a footer and the same CSS. Put them in one base template, and let each page fill in the parts that differ:

Python
import django
from django.conf import settings

settings.configure(TEMPLATES=[{
    "BACKEND": "django.template.backends.django.DjangoTemplates",
    "OPTIONS": {"loaders": [("django.template.loaders.locmem.Loader", {
        "base.html": (
            "<html><head><title>{% block title %}CodeMIRAI{% endblock %}</title></head>\n"
            "<body><header>CodeMIRAI</header>\n"
            "<main>{% block content %}{% endblock %}</main>\n"
            "<footer>Learn to code</footer></body></html>"
        ),
        "courses/course_list.html": (
            "{% extends 'base.html' %}\n"
            "{% block title %}Courses | {{ block.super }}{% endblock %}\n"
            "{% block content %}<h1>All courses</h1><ul>{% for c in courses %}<li>{{ c }}</li>{% endfor %}</ul>{% endblock %}"
        ),
    })]},
}])
django.setup()

from django.template.loader import render_to_string

print(render_to_string("courses/course_list.html", {"courses": ["Python", "SQL", "React"]}))
  • The base template marks replaceable areas with {% block name %}...{% endblock %}.
  • A child template starts with {% extends "base.html" %} and overrides only the blocks it needs.
  • {{ block.super }} inserts the parent's content for that block, here keeping "CodeMIRAI" in the title.

(The locmem loader in this example holds the template files in memory, standing in for files on disk.)

Reusing pieces: include

Python
import django
from django.conf import settings

settings.configure(TEMPLATES=[{
    "BACKEND": "django.template.backends.django.DjangoTemplates",
    "OPTIONS": {"loaders": [("django.template.loaders.locmem.Loader", {
        "courses/card.html": "<div class='card'><h3>{{ course.title }}</h3><p>{{ course.lessons }} lessons</p></div>",
        "courses/grid.html": "{% for course in courses %}{% include 'courses/card.html' %}\n{% endfor %}",
    })]},
}])
django.setup()

from django.template.loader import render_to_string

courses = [{"title": "Python", "lessons": 64}, {"title": "SQL", "lessons": 23}]
print(render_to_string("courses/grid.html", {"courses": courses}))

{% include %} inserts another template, which sees the same context, including the loop variable. It's the way to reuse a course card, a navigation bar or a form layout on many pages.

Where Django looks

Python
TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [BASE_DIR / "templates"],     # project-wide templates, such as base.html
        "APP_DIRS": True,                     # plus each app's templates/ folder
        "OPTIONS": {"context_processors": [...]},
    },
]

A typical project keeps base.html in a top-level templates/ folder listed in DIRS, and each app's own pages inside the app.

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