~/TechPurAI
~/tutorials/django-template-views
beginner·standalone·5 min read

Django template views: turning a URL into rendered HTML

Updated Aug 14, 2026Python · Django

A Django view's simplest job is turning a URL into an HTML page: take a request, render a template, return it as a response. This covers that whole path — a function-based view first, since it's what render() actually does under the hood, then TemplateView, the class-based shortcut for exactly this pattern, plus the template syntax and inheritance every real page ends up using.

Before this

This assumes a Django project and app already exist (django-admin startproject mysite && cd mysite && python manage.py startapp pages), with pages added to INSTALLED_APPS in settings.py. Everything below builds inside that pages app.

A minimal function-based view

python
# pages/views.py
from django.shortcuts import render

def home(request):
    return render(request, "pages/home.html")

render() is doing three things in one call: loading the template file, rendering it (with no context data yet), and wrapping the result in an HttpResponse. Every other pattern in this tutorial — including TemplateView — is a variation on exactly this.

Wiring it up: urls.py

python
# pages/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path("", views.home, name="home"),
]
python
# mysite/urls.py
from django.urls import path, include

urlpatterns = [
    path("", include("pages.urls")),
]

include("pages.urls") is what makes the app's own urlpatterns reachable from the project's root URLconf — without it, Django never sees the routes defined inside pages/urls.py at all. The name="home" on the route lets templates and other views reference this URL by name ({% url "home" %}) instead of hardcoding the path, so renaming the route later doesn't mean hunting down every hardcoded link to it.

Passing data to the template

python
def home(request):
    context = {
        "page_title": "Welcome",
        "articles": ["First post", "Second post", "Third post"],
    }
    return render(request, "pages/home.html", context)
django
{# pages/templates/pages/home.html #}
<h1>{{ page_title }}</h1>
<ul>
  {% for article in articles %}
    <li>{{ article }}</li>
  {% endfor %}
</ul>

context is a plain dict — every key becomes a variable available inside the template with {{ }}. {% for %} / {% endfor %} is a template tag, not Python; template tags handle control flow (loops, conditionals, includes) while {{ }} only ever outputs a value. Django looks for pages/home.html inside a templates/ directory on each app's own path by default (pages/templates/pages/home.html here) — the repeated pages/ in that path is a namespacing convention, not a typo, and it's what stops two apps that both have a home.html from silently shadowing each other.

Conditionals and filters

django
<p>
  {% if articles %}
    {{ articles|length }} article{{ articles|length|pluralize }}
  {% else %}
    No articles yet.
  {% endif %}
</p>

{% if %} works exactly like a Python conditional — truthy/falsy, no == needed for a plain existence check. The | syntax is a filter: articles|length is roughly len(articles), and filters chain left to right, so articles|length|pluralize feeds the count straight into pluralize, which prints "s" when the count isn't 1 and nothing when it is.

Common mistake

Writing template logic that belongs in the view instead — nested filter chains standing in for what should be a computed value passed through context. If a template line is hard to read, that's usually a sign the calculation belongs in Python, not in the template.

Template inheritance

django
{# pages/templates/pages/base.html #}
<!DOCTYPE html>
<html>
<head><title>{% block title %}My Site{% endblock %}</title></head>
<body>
  <nav>...</nav>
  {% block content %}{% endblock %}
</body>
</html>
django
{# pages/templates/pages/home.html #}
{% extends "pages/base.html" %}

{% block title %}Welcome{% endblock %}

{% block content %}
  <h1>{{ page_title }}</h1>
{% endblock %}

{% extends %} must be the first tag in the file — Django rejects the template otherwise. base.html defines named {% block %} regions; every other template {% extends %} it and fills in only the blocks it cares about, inheriting everything else (the <nav>, the <!DOCTYPE>, the overall page shell) unchanged. This is what stops every page in a real project from repeating the same HTML skeleton.

Switching to TemplateView

python
# pages/views.py
from django.views.generic import TemplateView

class HomeView(TemplateView):
    template_name = "pages/home.html"

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["page_title"] = "Welcome"
        context["articles"] = ["First post", "Second post", "Third post"]
        return context
python
# pages/urls.py
urlpatterns = [
    path("", views.HomeView.as_view(), name="home"),
]

TemplateView is the class-based shortcut for the exact function from the top of this tutorial: set template_name, and it handles the render() call for you. Context data goes in get_context_data() instead of a plain dict — and super().get_context_data(**kwargs) has to run first and get merged in, not skipped, since that's what carries through anything Django or a parent class already put in context (URL kwargs among them). .as_view() in the URLconf is what turns the class into something Django can actually route a request to; a bare views.HomeView (without the call) is a routing error, not a working view.

When TemplateView earns its keep

A function-based render() call and a TemplateView do the same thing here — the choice is about what the view needs to do beyond rendering. TemplateView is worth reaching for when a page's entire job is "render this template with some context," since it removes the boilerplate around that one job. The moment a view needs to branch on HTTP method (handle a POST differently from a GET), that's a sign to reach for a different generic view — or drop back to a function view, which stays more directly readable once there's real logic involved rather than pure templating.

VK

Vijay Kumar

Founder of TechPurAI — writing hands-on tutorials and honest tool breakdowns.

LinkedIn ↗
next in django →Starting a Django project: installation and project structure