Mengaktifkan dan mengkustomisasi Django Admin sebagai CMS production: ModelAdmin dengan list_display, list_filter, search_fields, dan list_editable, inline untuk relasi, custom actions, serta hardening akses admin agar aman di produksi.

Salah satu "batteries-included" paling terkenal yang kita bahas di episode 1 adalah Django Admin — panel CRUD otomatis yang lahir dari model. Di episode 7 database sudah punya tabel; sekarang kita menaiki layer di atasnya: mengubah admin bawaan menjadi CMS yang benar-benar nyaman dipakai editor dan aman di produksi.
Mengapa topik ini penting? Karena admin yang baik adalah produk tersendiri. list_display, filter, search, inline, dan custom actions adalah perbedaan antara "panel yang bisa dipakai" dan "panel yang bikin editor frustrasi". Ditambah, admin adalah salah satu permukaan serangan paling populer — jadi hardening-nya wajib dipahami.
Admin sudah aktif sejak startproject (terdaftar di INSTALLED_APPS dan urls.py). Buat superuser lalu masuk:
python manage.py createsuperuser
python manage.py runserverBuka http://127.0.0.1:8000/admin, login dengan kredensial tadi. Kalian akan melihat model Users, Groups, dan — setelah mendaftarkan model di admin.py — model blog kita.
from django.contrib import admin
from .models import Category, Post, Tag
@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
list_display = ("name", "slug")
@admin.register(Tag)
class TagAdmin(admin.ModelAdmin):
list_display = ("name",)
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
pass@admin.register(Model) menggantikan admin.site.register(Model, ModelAdmin) dan menjaga pasangan model-admin tetap dekat. Tanpa registrasi, model tidak muncul di admin.
list_display menentukan kolom yang tampil di halaman daftar. Inilah pengaturan yang paling terasa bagi editor:
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ("title", "status", "category", "author", "created_at")
list_filter = ("status", "category", "created_at")
search_fields = ("title", "slug", "body")
list_editable = ("status",)
list_per_page = 25
date_hierarchy = "created_at"
ordering = ("-created_at",)list_filter menambah sidebar filter — panel di kanan untuk memfilter berdasarkan status, category, atau tanggal.search_fields membuat kotak pencarian; field CharField/TextField menghasilkan pencarian LIKE, sementara FK menghasilkan pencarian id.list_editable mengizinkan edit langsung dari halaman daftar — sangat nyaman untuk mengubah status banyak post tanpa membuka halaman detail.date_hierarchy menghasilkan navigasi "drill-down" per tahun/bulan/hari.Tip
Jangan terlalu banyak list_editable — kolom editable dihitung dalam satu save, dan setiap kolom yang bukan list_display atau list_editable tetap tampil sebagai link. Untuk status massal, custom action (di bawah) sering lebih tepat daripada list_editable.
Inline memungkinkan mengelola relasi banyak-ke-satu langsung dari halaman model induk. Untuk komentar dalam halaman post:
from .models import Comment
class CommentInline(admin.StackedInline):
model = Comment
extra = 0
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
inlines = [CommentInline]StackedInline menampilkan form bertumpuk vertikal; TabularInline menampilkan tabel ringkas. extra = 0 mencegah tiga form kosong muncul default — hanya form yang benar-benar diisi yang ditambahkan.
Actions adalah operasi batch yang dijalankan pada baris yang dipilih di halaman daftar. Django punya aksi bawaan "delete selected"; kita tambahkan aksi publish/unpublish:
@admin.action(description="Publish post terpilih")
def publish_posts(modeladmin, request, queryset):
updated = queryset.update(status="published")
modeladmin.message_user(request, f"{updated} post dipublikasikan.")
@admin.action(description="Tarik kembali post terpilih")
def unpublish_posts(modeladmin, request, queryset):
queryset.update(status="draft")
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ("title", "status", "category", "author", "created_at")
actions = [publish_posts, unpublish_posts]queryset.update(...) menghasilkan satu query UPDATE — bukan loop save. @admin.action(description=...) menggantikan atribut actions yang lama dan lebih eksplisit. Action menerima tiga argumen: modeladmin, request, dan queryset yang terpilih.
Kadang logika harus berbeda antara admin dan form publik. Override method model yang dipanggil admin:
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ("title", "status", "author", "created_at")
def save_model(self, request, obj, form, change):
if not change:
obj.author = request.user
super().save_model(request, obj, form, change)save_model di-override agar author otomatis diisi user yang sedang login saat membuat post baru — mengikuti prinsip "audit trail" tanpa memaksa editor memilih nama sendiri.
Admin adalah permukaan serangan bernilai tinggi. Empat langkah minimum sebelum produksi:
/admin/ dengan nama default — ganti prefix di urls.py:from django.contrib import admin
from django.urls import path
urlpatterns = [
path("secure-console/", admin.site.urls),
]Batasi akses via middleware atau reverse proxy — admin hanya boleh diakses dari IP kantor/VPN. Ini kita kerjakan di episode 18 dan 23.
Matikan di environment non-staf — jika app tidak butuh admin di produksi, jangan daftarkan URL admin di urls.py produksi sama sekali.
Pakai login yang kuat — aktifkan rate limiting login dan 2FA. Implementasinya kita bahas di episode 19 dan 20.
Warning
django.contrib.admin memakai model User bawaan yang di-refer oleh auth. Jika kalian berencana memakai custom user model (best practice di episode 10), keputusan itu harus dibuat sebelum migrasi pertama. Mengganti user model setelah database berisi data adalah operasi yang sangat menyakitkan.
Inti yang harus dibawa pulang:
@admin.register; model tak terdaftar tak muncul di admin.list_display, list_filter, search_fields, dan list_editable membuat daftar post produktif.extra = 0 mencegah form kosong default.@admin.action) memberi operasi batch sekali query.save_model untuk logika khusus admin (misal set author otomatis).Di episode 9 selanjutnya kita melengkapi sisi input data: Forms & Validation — Form dan ModelForm, validasi bersih dan validasi per-field, widgets, error rendering, serta form komentar dengan proteksi CSRF. Admin selesai, sekarang giliran form publik!