Views and URLs
URL routing with converters and namespaces, function and class-based views, and returning JSON from the same project.
URL configuration
# blog/urls.py
from django.urls import path
from . import views
app_name = "blog" # namespace, used as blog:detail
urlpatterns = [
path("", views.post_list, name="list"),
path("<int:pk>/", views.post_detail, name="detail"),
path("<slug:slug>/", views.post_by_slug, name="by-slug"),
path("category/<slug:slug>/", views.category, name="category"),
]# config/urls.py
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path("admin/", admin.site.urls),
path("blog/", include("blog.urls")),
path("api/", include("api.urls")),
]| Converter | Matches | Example |
|---|---|---|
str | Any text except / (default) | hello-world |
int | Non-negative integer | 42 |
slug | Letters, digits, hyphens, underscores | my-post |
uuid | Formatted UUID | a1b2c3d4-... |
path | Any text including / | a/b/c.txt |
Function and class-based views
# blog/views.py
from django.http import Http404, JsonResponse
from django.shortcuts import get_object_or_404, render
from django.views.generic import DetailView, ListView
from .models import Post
def post_list(request):
posts = Post.objects.select_related("author").filter(status="published")
return render(request, "blog/post_list.html", {"posts": posts})
def post_detail(request, pk):
post = get_object_or_404(Post, pk=pk) # 404 instead of DoesNotExist
return render(request, "blog/post_detail.html", {"post": post})
def post_json(request, pk):
post = get_object_or_404(Post, pk=pk)
return JsonResponse({"id": post.pk, "title": post.title})
class PostDetailView(DetailView):
model = Post
template_name = "blog/post_detail.html"
context_object_name = "post"
def get_queryset(self):
return Post.objects.select_related("author").filter(status="published")
class PublishedListView(ListView):
queryset = Post.objects.filter(status="published")
paginate_by = 20
context_object_name = "posts"# blog/models.py
from django.db import models
from django.urls import reverse
class Post(models.Model):
...
def get_absolute_url(self):
return reverse("blog:detail", kwargs={"pk": self.pk})- A view receives an
HttpRequestand returns anHttpResponse(or raises an exception). - Generic views (
ListView,DetailView,CreateView) are worth it once you use pagination, forms and 404 handling; plain functions are clearer below that threshold. - Build links with
{% url 'blog:detail' pk=post.pk %}orreverse()so renaming a path does not break templates.
Status codes and errors
from django.http import JsonResponse
from django.views.decorators.http import require_POST
@require_POST
def create_comment(request, pk):
post = get_object_or_404(Post, pk=pk)
text = request.POST.get("text", "").strip()
if not text:
return JsonResponse({"error": "text is required"}, status=400)
comment = post.comment_set.create(text=text)
return JsonResponse({"id": comment.pk}, status=201)
def handler404(request, exception):
return render(request, "404.html", status=404)💡
Django only uses
handler404 and handler500 when DEBUG = False. Test your 404 page with the debug flag off, otherwise you will discover the undefined template name in production.FAQ
Why does my URL not match?
Check the trailing slash —
APPEND_SLASH redirects a GET from /blog/1 to /blog/1/ but a POST body is lost on a 301. Declare the slash you actually want and be consistent.Function or class-based view?
Functions for simple, explicit logic; class-based generic views when you need the built-in pagination, form and object lookup machinery. You can mix both freely in one app.
Related
Project layout and the ORM The admin and forms
Last refreshed 2026-09-18.