Django Admin'i Müşterinin Kullanabileceği İçerik Paneline Dönüştürmek
Bir müşteri projesinde en pahalı kararlardan biri, içerik yönetimi için sıfırdan bir panel yazmaya karar vermektir. Haftalar gider, sonuçta Django'nun kutudan çıkan admin panelinin yaptığı işi yapan bir şey elde edersiniz.
Çoğu durumda gerekli olan şey yeni bir panel değil, mevcut admin'i teknik olmayan birinin rahatça kullanabileceği hale getirmektir. Bu yazıda bunu yapan somut ayarları anlatıyorum.
Liste ekranını okunabilir hale getirmek
Varsayılan admin listesi her kaydı __str__ çıktısıyla gösterir. On kayıtta sorun değil, binlerce kayıtta kullanılamaz hale gelir.
from django.contrib import admin
@admin.register(Listing)
class ListingAdmin(admin.ModelAdmin):
list_display = ("title", "city", "price", "is_published", "updated_at")
list_filter = ("is_published", "city", "category")
search_fields = ("title", "description")
ordering = ("-updated_at",)
list_per_page = 50Bu beş satır, listeyi "kayıt yığını"ndan "çalışılabilir tablo"ya çevirir. list_filter sağ tarafta filtre paneli açar, search_fields üstte arama kutusu koyar.
Bir uyarı: search_fields'a çok sayıda alan eklemek, her aramada OR zinciri üreteceği için büyük tablolarda yavaşlar. Gerçekten arananları koyun.
Doğrudan listeden düzenleme
İçerik ekibinin en sık yaptığı işlem genelde tek bir alanı değiştirmektir — bir ilanı yayından kaldırmak gibi. Her seferinde detay sayfasına girmek yerine:
list_editable = ("is_published",)Artık liste ekranındaki onay kutusundan doğrudan değiştirebilirler. list_editable içindeki alanların list_display'de de bulunması gerekir ve ilk sütun olamaz.
Toplu işlemler
Bazı işler tek tek yapılamayacak kadar tekrarlı olur. Özel bir admin action bunu çözer:
@admin.action(description="Seçili kayıtları yayınla")
def publish_selected(modeladmin, request, queryset):
updated = queryset.update(is_published=True)
modeladmin.message_user(request, f"{updated} kayıt yayınlandı.")
class ListingAdmin(admin.ModelAdmin):
actions = [publish_selected]queryset.update() kullanmak tek sorguda çalışır — döngüyle save() çağırmaktan çok daha hızlıdır. Ancak dikkat: update() model sinyallerini tetiklemez. Eğer kaydetme sırasında çalışan bir sinyaliniz varsa (ör. önbellek temizleme), döngü kullanmanız veya sinyali elle çağırmanız gerekir.
Formu anlaşılır bölümlere ayırmak
Yirmi alanlı bir model, admin formunda yirmi satırlık bir liste olarak görünür. Hangi alanın neyle ilgili olduğu belli olmaz:
fieldsets = (
("Temel Bilgiler", {
"fields": ("title", "slug", "category"),
}),
("Fiyatlandırma", {
"fields": ("price", "currency"),
}),
("SEO", {
"fields": ("meta_title", "meta_description"),
"classes": ("collapse",),
"description": "Boş bırakılırsa başlık ve açıklamadan otomatik üretilir.",
}),
)collapse sınıfı bölümü katlanmış başlatır — nadiren dokunulan alanları gizler. description alanı, kullanıcının "burayı doldurmak zorunda mıyım" sorusunu sormadan cevaplar.
İlişkili kayıtları aynı ekranda düzenlemek
Bir ilana ait fotoğrafları ayrı bir menüden yönetmek zorunda kalmak can sıkıcıdır. Inline bunu çözer:
class ListingImageInline(admin.TabularInline):
model = ListingImage
extra = 1
fields = ("image", "alt_text", "order")
class ListingAdmin(admin.ModelAdmin):
inlines = [ListingImageInline]TabularInline kompakt bir tablo verir, StackedInline ise her kaydı alt alta form olarak gösterir. Alan sayısı azsa tabular, çoksa stacked daha okunaklıdır.
Çok dilli içeriği yönetilebilir kılmak
Bir sitede içeriğin 15 dilde tutulması gerektiğinde, her dil için ayrı alan açmak modeli şişirir. Yaygın yaklaşım, çeviriyi ayrı bir modelde tutup inline ile bağlamaktır:
class RouteTranslation(models.Model):
route = models.ForeignKey("Route", on_delete=models.CASCADE,
related_name="translations")
language = models.CharField(max_length=5, choices=LANGUAGES)
title = models.CharField(max_length=200)
description = models.TextField(blank=True)
class Meta:
unique_together = ("route", "language")
class RouteTranslationInline(admin.TabularInline):
model = RouteTranslation
extra = 0unique_together aynı dilin iki kez eklenmesini veritabanı seviyesinde engeller. İçerik ekibi tek ekrandan tüm dilleri görüp düzenler.
Kaydetmeden önce doğrulama
İçerik girenler her zaman kuralları hatırlamaz. Kuralı modele koymak, admin'de otomatik hata mesajına dönüşür:
from django.core.exceptions import ValidationError
class Route(models.Model):
price_min = models.DecimalField(max_digits=10, decimal_places=2)
price_max = models.DecimalField(max_digits=10, decimal_places=2)
def clean(self):
if self.price_max < self.price_min:
raise ValidationError({
"price_max": "Üst fiyat, alt fiyattan küçük olamaz.",
})Hata sözlük olarak verildiğinde admin, mesajı ilgili alanın altında gösterir. Sadece string verirseniz formun en üstünde çıkar ve kullanıcı hangi alanı düzelteceğini aramak zorunda kalır.
Yayın akışını görünür kılmak
Taslak/yayında ayrımı olan bir sistemde, durumu renkli göstermek listeyi taranabilir yapar:
from django.utils.html import format_html
@admin.display(description="Durum")
def status_badge(self, obj):
color = "#0d7d72" if obj.is_published else "#94a3b8"
label = "Yayında" if obj.is_published else "Taslak"
return format_html(
'<span style="color:{};font-weight:600">{}</span>', color, label
)format_html kullanmak önemlidir — string birleştirmek yerine bunu kullanmak, içeriğin HTML olarak kaçırılmasını (escape) sağlar ve XSS riskini önler.
Yetkilendirmeyi unutmayın
İçerik ekibinin kullanıcı silme veya ayar değiştirme yetkisi olmamalı. Django'nun grup sistemi bunun için yeterlidir: bir "İçerik Editörü" grubu oluşturup sadece ilgili modellerin add, change, view izinlerini verin. delete iznini vermemek çoğu senaryoda doğru varsayılandır — yanlışlıkla silinen içerik geri gelmez.
Ek olarak admin panelinin adresini varsayılan yolda bırakmamak, otomatik tarama botlarının büyük kısmını eler:
# config/urls.py
urlpatterns = [
path("<tahmin-edilemez-yol>/", admin.site.urls),
]Bunun gerçek bir güvenlik katmanı olmadığını unutmayın — asıl koruma güçlü parolalar, iki faktörlü doğrulama ve ALLOWED_HOSTS gibi ayarlardır. Yol değiştirmek sadece gürültüyü azaltır.
Ne zaman yetmez
Django admin her senaryoyu karşılamaz. Şu durumlarda ayrı bir arayüz yazmak gerekebilir:
- Kullanıcının sürükle-bırak ile sıralama yapması gerekiyorsa
- Aynı ekranda birden fazla modelin karmaşık bir akışla düzenlenmesi gerekiyorsa
- Panel son kullanıcıya (müşterinin müşterisine) açılacaksa
Bunların dışında, yukarıdaki ayarlarla admin çoğu içerik yönetimi ihtiyacını karşılar ve size haftalarca geliştirme süresi kazandırır.