Django

URLs and views — the basics

How Django decides which piece of your code should handle a given web address, and the two shapes a view can take to actually respond to it.

Beginner

3 min read

urls.py: a list of patterns, matched top to bottom

# urls.py
from django.urls import path
from . import views
 
urlpatterns = [
    path("", views.home, name="home"),
    path("articles/", views.article_list, name="article-list"),
    path("articles/<int:article_id>/", views.article_detail, name="article-detail"),
]

Django checks each path() entry in order and uses the first one whose pattern matches the requested URL, calling the associated view function. <int:article_id> is a path converter — it matches a segment of the URL that's an integer, and passes it into the view function as an argument named article_id; visiting /articles/5/ calls views.article_detail(request, article_id=5). The name= argument gives that URL pattern a stable name you can reference elsewhere in code (in a template, or via reverse("article-detail", args=[5])) without hardcoding the literal path string — so the actual URL can change later without breaking every link that points to it.

A function-based view: the simplest shape

# views.py
from django.http import HttpResponse
from django.shortcuts import render, get_object_or_404
from .models import Article
 
def home(request):
    return HttpResponse("Welcome!")
 
def article_detail(request, article_id):
    article = get_object_or_404(Article, id=article_id)
    return render(request, "articles/detail.html", {"article": article})

A view is just a Python function that takes request (an object describing the incoming request — method, headers, query parameters, and more) as its first argument, and returns some kind of HttpResponse. HttpResponse("Welcome!") sends raw text back directly. render(request, template_name, context) is the far more common pattern: it takes an HTML template file and a dictionary of data (the context), fills the template's placeholders in with that data, and wraps the result in an HttpResponse automatically. get_object_or_404 is a small but very common shortcut: fetch an object, and if it doesn't exist, return a proper 404 page instead of letting an unhandled exception crash the request.

What a template actually looks like

{# articles/detail.html #}
<h1>{{ article.title }}</h1>
<p>{{ article.body }}</p>
{% if article.is_featured %}
  <span class="badge">Featured</span>
{% endif %}

{{ variable }} inserts a value from the context dictionary directly into the HTML. {% tag %} runs template logic — if, for loops over a list, and others — inside the HTML itself. This is Django's template language: deliberately less powerful than writing raw Python inside a template, on purpose, to keep presentation logic separated from application logic.

The GET vs. POST distinction, and why it matters immediately

def contact_form(request):
    if request.method == "POST":
        # process the submitted form data
        name = request.POST.get("name")
        return HttpResponse(f"Thanks, {name}!")
    return render(request, "contact.html")   # GET — just show the empty form

request.method tells a view how it was reached — GET for a normal page visit or link click, POST for a submitted form. The same URL and the same view function commonly handle both: show the form on GET, process the submission on POST. This branch — checking request.method before deciding what to do — is one of the most common shapes in real Django view code.

Class-based views: the same idea, packaged differently

from django.views.generic import DetailView
 
class ArticleDetailView(DetailView):
    model = Article
    template_name = "articles/detail.html"
# urls.py
path("articles/<int:pk>/", views.ArticleDetailView.as_view(), name="article-detail"),

A class-based view achieves the same thing as the function-based article_detail above, but through inheritance: DetailView already knows how to fetch one object by its primary key and render it with a template, so this whole view is three lines instead of writing the fetch-and-render logic out by hand. This convenience is exactly the function-based-vs-class-based trade-off — explicit and readable top-to-bottom, versus less code but more indirection through a parent class — that gets its own deeper look once you're comfortable with the function-based basics here.

Further reading

Check your understanding

A quick comprehension check — not tracked, not graded, just for you.

1. In `path("articles/<int:article_id>/", views.article_detail)`, what does `<int:article_id>` do?

2. What's the difference between HttpResponse("text") and render(request, template, context)?

3. Why do many Django views check `if request.method == "POST":`?

4. What does {% if article.is_featured %} inside a Django template actually do?