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.
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 formrequest.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?