Views, URLs & templates

Every Django page follows the same path: a URL pattern matches, a view function runs, it fetches data and returns a rendered template. Get comfortable with this cycle and the rest of the framework slots around it.

The view

A view takes a HttpRequest and returns a HttpResponse. render() does the common case — load a template, fill it with a context dictionary, return HTML.

# shop/views.py
from django.core.paginator import Paginator
from django.shortcuts import render, get_object_or_404
from .models import Product


def product_list(request):
    q = request.GET.get("q", "").strip()
    products = Product.objects.filter(in_stock=True).select_related("category")
    if q:
        products = products.filter(name__icontains=q)

    page = Paginator(products, 12).get_page(request.GET.get("page"))
    return render(request, "shop/list.html", {"page": page, "q": q})


def product_detail(request, slug):
    product = get_object_or_404(Product, slug=slug, in_stock=True)
    return render(request, "shop/detail.html", {"product": product})

get_object_or_404 returns a clean 404 instead of an exception page when the slug does not exist.

URLs

Give each app its own urls.py, name every pattern, and set app_name so names are namespaced.

# shop/urls.py
from django.urls import path
from . import views

app_name = "shop"
urlpatterns = [
    path("", views.product_list, name="product_list"),
    path("p/<slug:slug>/", views.product_detail, name="product_detail"),
]

# config/urls.py
from django.urls import include, path
urlpatterns = [path("shop/", include("shop.urls"))]
Converter Matches Example
str Any text without a slash (default) <str:city>
int Digits, passed as an int <int:pk>
slug Letters, numbers, hyphens, underscores <slug:slug>
uuid A UUID <uuid:token>
path Text including slashes <path:subpath>

Never hard-code a URL. Use reverse("shop:product_detail", args=[slug]) in Python and {% url 'shop:product_detail' p.slug %} in templates, so changing a route breaks nothing.

Templates

Put shared structure in a base template and extend it.

<!-- templates/base.html -->
<!doctype html>
<html lang="en">
<head><title>{% block title %}Kirana Store{% endblock %}</title></head>
<body>
  <header>{% include "partials/nav.html" %}</header>
  <main>{% block content %}{% endblock %}</main>
</body>
</html>
<!-- templates/shop/list.html -->
{% extends "base.html" %}
{% block title %}Products{% endblock %}
{% block content %}
  <form method="get"><input name="q" value="{{ q }}" placeholder="Search"></form>
  {% for p in page %}
    <p>
      <a href="{% url 'shop:product_detail' p.slug %}">{{ p.name }}</a>
      — {{ p.category.name }} — Rs {{ p.price|floatformat:2 }}
    </p>
  {% empty %}
    <p>No products matched "{{ q }}".</p>
  {% endfor %}
  {% if page.has_next %}<a href="?q={{ q }}&page={{ page.next_page_number }}">Next</a>{% endif %}
{% endblock %}

Useful filters: floatformat:2 for prices, date:"d M Y" for dates, truncatechars:80, default:"—", and intcomma from django.contrib.humanize once you add it to INSTALLED_APPS.

The template language is deliberately limited — no arbitrary Python. Business logic belongs in the view or a model method, not in the page.

Escaping keeps you safe

Django auto-escapes variables, so a product name containing <script> renders as text. Only mark content safe when you truly control it, and run any user-supplied HTML through a sanitiser first.

Common mistakes

  • Querying inside the template loop ({{ p.category.name }} without select_related) and firing one query per row.
  • Hard-coded URLs like /shop/p/kaju-katli/ scattered through templates.
  • Forgetting {% csrf_token %} inside a POST form.
  • Doing pagination in Python with slicing instead of Paginator, and loading the whole table into memory.
  • Putting {% load %} tags in the child template but forgetting them in the included partial.

Practice

Build a two-page mini catalogue: a list with search and 12-per-page pagination, and a detail page reached by slug. Confirm with the Django debug toolbar or assertNumQueries that the list page runs a constant number of queries no matter how many products it shows.

B

Written by Bablu Kumar Jha

Founder, BeeProton AI · Mumbai, Maharashtra, India