Menulis URLconf dengan urlpatterns dan path converters, memahami name- based reverse agar URL tidak di-hardcode, membandingkan Function-Based Views (FBV) dengan Class-Based Views (CBV), serta memakai HttpResponse dan JsonResponse untuk respons pertama.

Setelah project devblog berdiri di episode 3, sekarang kita mengisi dua lapisan pertama siklus request dari episode 2: URLconf dan Views. Di episode ini kalian menulis URL pertama, menangkap parameter dari path, dan menulis view yang mengembalikan HTML maupun JSON.
Mengapa topik ini penting? URL adalah kontrak antara aplikasi kalian dan dunia luar. Begitu sebuah URL dipakai pengguna, crawler, atau API client, mengubahnya seenaknya akan memutus banyak hal. Karena itu Django menyediakan sistem name-based reverse: kalian merujuk URL lewat nama, bukan string, sehingga URL bisa diubah kapan saja tanpa memutus kode lain.
URLconf adalah daftar pola yang memetakan URL ke view. Di devblog/urls.py (root), semua route project di-include per app:
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path("admin/", admin.site.urls),
path("", include("blog.urls")),
]include("blog.urls") menyerahkan penanganan path yang dimulai dengan "" ke file blog/urls.py. Sekarang buat file itu:
from django.urls import path
from . import views
urlpatterns = [
path("", views.home, name="home"),
path("posts/<int:pk>/", views.post_detail, name="post_detail"),
path("posts/<slug:slug>/", views.post_detail_by_slug, name="post_by_slug"),
]Perhatikan pola posts/<int:pk>/ dan posts/<slug:slug>/: <int:pk> adalah path converter yang hanya menerima integer dan meneruskannya sebagai parameter bernama pk. Converter bawaan Django:
| Converter | Menerima | Contoh |
|---|---|---|
str | string tanpa / (default) | hello |
int | bilangan bulat positif | 42 |
slug | huruf, angka, -, _ | belajar-django |
uuid | UUID v4 | 550e8400-e29b-... |
path | string apa pun termasuk / | dir/file.txt |
Tip
Jangan pernah memakai converter str longgar seperti posts/<pk>/ tanpa batasan jika parameter dipakai untuk query database — nanti pk bisa berupa string apa pun. Gunakan int atau slug sesuai domain data kalian; ini sekaligus mencegah request tak valid masuk jauh ke sistem.
Function-Based Views adalah cara paling lurus: fungsi Python yang menerima request dan mengembalikan respons.
from django.http import HttpResponse, JsonResponse
from django.shortcuts import render, get_object_or_404
from .models import Post
def home(request):
posts = Post.objects.filter(published=True)
return render(request, "blog/post_list.html", {"posts": posts})
def post_detail(request, pk):
post = get_object_or_404(Post, pk=pk)
return render(request, "blog/post_detail.html", {"post": post})render() merender template dengan konteks. get_object_or_404 menggabungkan query dan 404 handling: jika objek tidak ditemukan, otomatis melempar Http404 — mencegah error DoesNotExist mentah yang bocor ke user.
Untuk respons non-HTML, gunakan JsonResponse:
from django.http import JsonResponse
def healthz(request):
return JsonResponse({"status": "ok", "version": 1})JsonResponse mengubah dict menjadi JSON, mengatur header Content-Type: application/json, dan — penting — hanya menerima data JSON-serializable. Ini menjadi dasar REST API yang akan kita bangun penuh di episode 11.
Ketika logika view bertambah, FBV berisiko jadi panjang dan berulang. Django menyediakan Class-Based Views: view sebagai class yang logikanya dipecah ke method bernama.
from django.views.generic import ListView, DetailView
from .models import Post
class PostListView(ListView):
model = Post
template_name = "blog/post_list.html"
context_object_name = "posts"
queryset = Post.objects.filter(published=True)
class PostDetailView(DetailView):
model = Post
template_name = "blog/post_detail.html"ListView dan DetailView mengatur query, pagination, dan konvensi konteks untuk kalian. context_object_name mengganti nama variabel default (post_list → posts). Bandingkan ringkasnya dengan FBV di atas — untuk pola CRUD standar, CBV jelas lebih hemat baris.
| Aspek | FBV | CBV |
|---|---|---|
| Kejelasan | Lurus, mudah dibaca baris per baris | Logika tersebar di method (get, post, get_queryset) |
| Reuse | Perlu decorator/fungsi helper | Inheritance + mixin sangat mudah |
| Ekstensibilitas | Fungsi dengan args | Override method per-request |
| Cocok untuk | View sederhana & custom | Pola CRUD yang repetitif |
Note
Tidak ada pemenang mutlak antara FBV dan CBV — project nyata memakai keduanya. Aturan praktis: gunakan FBV untuk view sederhana dengan logika unik, CBV untuk pola list/detail/form yang berulang. Perbandingan lebih dalam kita bahas saat menulis form di episode 9.
Perhatikan name="post_detail" di blog/urls.py. Kini semua kode merujuk URL lewat nama, bukan string:
from django.urls import reverse
reverse("post_detail", args=[42]) # "/posts/42/"
reverse("post_by_slug", kwargs={"slug": "hello"}) # "/posts/hello/"Dan di dalam template, memakai tag {% url %}:
<a href="{% url 'post_detail' post.pk %}">{{ post.title }}</a>Manfaatnya: jika besok kalian mengubah posts/<int:pk>/ menjadi artikel/<int:pk>/, cukup ubah satu baris di urls.py — semua link, redirect, dan form tetap bekerja. Hardcode URL ("/posts/" + str(pk)) adalah salah satu dosa paling umum yang baru terasa saat restrukturisasi.
# blog/urls.py
app_name = "blog"
urlpatterns = [
path("", views.home, name="home"),
path("posts/<int:pk>/", views.post_detail, name="post_detail"),
]Dengan app_name = "blog", template memakai {% url 'blog:post_detail' post.pk %}. Namespace ini penting ketika banyak app punya nama view yang sama (home ada di app A dan app B) — namespace mencegah ambigu.
Jalankan server dan uji:
python manage.py runserver
curl -I http://127.0.0.1:8000/healthz/
curl -I http://127.0.0.1:8000/posts/999/curl -I menampilkan hanya header: endpoint /healthz/ harus mengembalikan 200, sementara /posts/999/ (objek belum ada) mengembalikan 404. Seluruh perilaku HTTP ini adalah kontrak yang akan kalian andalkan saat menulis test di episode 15 dan API di episode 11.
Inti yang harus dibawa pulang:
urlpatterns memetakan URL ke view; converter int/slug/uuid menyaring parameter.include() menghubungkan URLconf root ke URLconf per-app; app_name memberi namespace.get_object_or_404 dan JsonResponse menangani 404 dan JSON secara idiomatis.reverse/{% url %}), jangan hardcode URL.Di episode 5 selanjutnya kita masuk ke lapisan presentasi: Templates & Template Language — sintaks DTL, template inheritance dengan base.html, custom filters/tags, dan context processors untuk layout + partials yang bisa dipakai ulang. Sampai jumpa di episode 5!