18 Temmuz 2014 Cuma

ModelAdmin Seçenekleri

Bu yazı serimizde Django'nun bize sağlamış olduğu yönetim panelini özelleştirmekten bahsedeceğiz. Django ile birlikte gelen güçlü bir yönetim paneli bulunmaktadır. Ama işin asıl güzel kısmı bu yönetim panelini isteklerimiz doğrultusunda şekillendire biliyoruz.
date_hierarchy
Elimizde bulunan bir modelin listelemesi yapılırken tarih bazlı filtreleme yapılması isteniyorsa bu seçenek kullanılabilir. Elimizde şöyle bir model olduğunu düşünelim.
class Kisi(models.Model):
    isim = models.CharField(max_length=100)
    dogum_tarihi  = models.DateField()

    def __unicode__(self):
        return self.isim
Şimdi admin.py dosyasına şu kodları yazıyoruz.
class KisiAdmin(admin.ModelAdmin):
    date_hierarchy = 'dogum_tarihi'

admin.site.register(Kisi,KisiAdmin)
Bu ayarlamaları yaptıktan sonra yönetim panelinde Kisi modeline girerseniz en üstte girdiğiniz tarihleri görebilirsiniz. Bu tarihlere tıklayarak sadece o tarihlere ait kişileri listeleye bilirsiniz.
Resimde 1992 ve 1993 sayılarını görebiliyorsunuz.
ordering
ordering seçeneği ile listeleme yapılırken hangi alana göre sıralama yapılacağını seçebiliriz. Listeleme için ordering seçeneğini de ekleyelim.
class KisiAdmin(admin.ModelAdmin):
    date_hierarchy = 'dogum_tarihi'
    ordering = ('isim', )
Ali ve Veli'nin yerlerinin değiştiğini görebilirsiniz.
actions
Listelenen elemanları seçerek toplu bir şekilde işlem yapabiliyoruz. Django'da varsayılan olarak sadece "Seçilileri sil" seçeneği gelmektedir. Ama biz seçilen elemanlar üzerinde başka işlemler yaptırmak isteyebiliriz. Bunun için actions seçeneğini kullanacağız. Çok mantıklı olmasa da göstermek açısından kullanıcı doğum tarihlerini günümüz tarihi olarak güncelleyen bir seçenek ekleyelim.
import time
def yil_duzenle(modeladmin,request,queryset):
    queryset.update(dogum_tarihi=time.strftime("%Y-%m-%d"))
yil_duzenle.short_description = 'Doğum tarihlerini güncelle'

class KisiAdmin(admin.ModelAdmin):
    actions = [yil_duzenle]

admin.site.register(Kisi,KisiAdmin)
Kodlaru bu şekilde düzenledikten sonra seçenekler listesinde "Doğum tarihlerini güncelle" seçeneğini şu şekilde göreceksiniz.
Seçtiğiniz kullanıcıların doğum tarihleri güncellenecektir.
actions_on_top & actions_on_bottom
Seçenekler kutusunun nerede duracağına karar vermemizi sağlar. Varsayılan olarak şöyle gelir.
    actions_on_bottom = False
    actions_on_top = True
Eğer her ikisi de True yapılırsa hem listenin üstünde hemde altında gözükecektir.
actions_selection_counter
Yukarıda ki resmi incelerseniz "İki nesne arasından seçim.." diye bir alan göreceksiniz. Burada seçili olan nesnelerin sayısı hakkında bilgi verilmektedir. Bu actions_selection_counter seçeneği ile yapılmaktadır. Varsayılan olarak True gelmektedir. Eğer görünmesini istemiyorsak False yapabiliriz.
class KisiAdmin(admin.ModelAdmin):
    actions = [yil_duzenle]
    actions_selection_counter = False

fields & exlude
Modelden ekleme işlemi yaparken fields ile gösterilecek alanları exlude ile ise gösterilmeyecek alanları belirleyebiliyoruz. Örneğin elimizde şöyle bir model olduğunu düşünelim.
class Yazi(models.Model):
    baslik  = models.CharField(max_length=100)
    tarih   = models.DateTimeField()
    taslak  = models.BooleanField(default=False)
Burada yazi ekleme işlemi yapılırken sadece "baslik" be "tarih" alanlarının görüntülenmesini istersek;
class YaziAdmin(admin.ModelAdmin):
    fields = ('baslik', 'tarih')

admin.site.register(Yazi,YaziAdmin)
Aynı işlemi exlude ile de yapabiliriz. Göstermek istemediğimiz alan "taslak" olduğu için onu yazmalıyız.
class YaziAdmin(admin.ModelAdmin):
    exclude = ('taslak', )
Aynı işlemi görecektir. Eğer iki alanın aynı satırda gözükmesini istersek fields özelliği içerisinde alanları demet içerisinde yazabiliriz.
class YaziAdmin(admin.ModelAdmin):
    fields = (('baslik', 'taslak'), 'tarih', )

fieldsets
Alan ekleme sayfasını parçalamak istersek bu özelliği kullanabiliriz. Örneğin eklem sayfasında zorunlu alanlar ile zorunlu olmayan alanları altlı üstlü iki parça olarak göstermek istersek şöyle bir kullanım işimizi görecektir. Bir önceki modelden devam edelim.
class YaziAdmin(admin.ModelAdmin):
    fieldsets = (
        ('Mecburi Alanlar',{
            'fields':('baslik', 'tarih')
        }),
        ('Seçmeli Alanlar',{
            'classes': ('collapse', ),
            'fields' : ('taslak', ),
        })
    )
Burada sayfayı ikiye böldük. Demetin ilk elemanıda bir demet ve bu demetin ilk elamanı ise bölümün başlığını içeriyor. Burada dikkat ettiyseniz "Seçmeli Alanlar" kısmında classes diye bir alan kullandık. Bu bölüme stil atamamızı sağlıyor. Burada atadığımız stil Django'nun kendisinde bulunan collapse. Bu stil sayesinde bölüme Göster/Gizle butonu gelecektir.

css & js
Eğer istersek model sayfasında yüklenecek css ve js dosyalarının tanımını yapabiliriz.
class YaziAdmin(admin.ModelAdmin):
    class Media:
        css = {
            "all":("style.css", )
        }
        js = ("code.js", )
Buradaki style.css ve code.js dosyaları "static" dizini içerisinde aranır. Yani /static/ linkine hangi dizini tanımladıysak o dosya içerisinde arama yapılır.
filter_horizontal
Model içerisinde bulunan ManyToManyField alanları için gelişmiş seçim alanı sunar. Bu alanı Django'nun User modeli gibi alanlarda görmüştük. Bu seçenek seçenek sadece ManyToManyField alanlar için kullanılabilir.
class YaziAdmin(admin.ModelAdmin):
    filter_horizontal = ('kategori', )

filter_vertical
filter_horizontol ile aynı işlemi yapmaktadır. Sadece seçim alanları dikey gözükmektedir.
forms
Django yönetim panelindeki modele ekleme yaparken ekleme işleminin oluşturduğumuz bir forma göre yapılmasını istersek bu seçeneği kullanabiliriz. Örneğin oluşturduğumuz Yazi modeline ekleme yapılırken başlığının tekrarlanmasını engelleyen bir form oluşturalım ve modele bu formu gösterelim.
class YaziForm(forms.ModelForm):
    def clean_baslik(self):
        baslik = self.cleaned_data.get('baslik')
        if Yazi.objects.filter(baslik=baslik).exclude(id=self.instance.id).count():
            raise forms.ValidationError('Bu başlıkta bir yazı bulunmaktadır.')
        return baslik
class YaziAdmin(admin.ModelAdmin):
    list_display = ('baslik', )
    filter_vertical = ('kategori', )
    form = YaziForm
admin.site.register(Yazi, YaziAdmin)
Burada bir formu oluşturduk ve bu formu YaziAdmin içerisinde gösterdik. Artık bir yazı kaydedilirken bu form dikkate alınacak ve aynı başlıktan birden fazla yazı kaydedilmeyecektir.

formfield_ovverides
Form alanlarını özelleştirmek için kullanabiliriz. Örneğin elimizdeki yazı modeline içerik kısmı ekleyelim.
class Yazi(models.Model):
    baslik  = models.CharField(max_length=100)
    icerik = models.TextField()
    tarih   = models.DateTimeField()
    taslak  = models.BooleanField(default=False)
    kategori = models.ManyToManyField(Kategori)
Bu modeldeki TextField alanları için CKEditor kullanmak istediğimizi düşünürsek bu özellik bizim işimizi görebilir.
from django.db import models
from ckeditor.widgets import CKEditorWidget

class YaziAdmin(admin.ModelAdmin):
    list_display = ('baslik', )
    filter_vertical = ('kategori', )
    form = YaziForm
    formfield_overrides = {
        models.TextField : {'widget':CKEditorWidget}
    }

admin.site.register(Yazi, YaziAdmin)
Formda bulunan tüm TextField alanları artık CKEditor olacaktır. İşte bu tarz özelleştirmeler için kullanbileceğimiz bir özelliktir.
CKEditör'ün Django'da nasıl kullanıldığı bu yazının konusu olmadığı için anlatılmayacaktır. Kullanımına buradan bakabilirsiniz.
Veya şöyle kullanarak Model içerisindeli CharField alanlarına class ataması yapabilirsiniz.
class YaziAdmin(admin.ModelAdmin):
    formfield_overrides = {
        models.TextField : {'widget':CKEditorWidget},
        models.CharField : {'widget':forms.TextInput(attrs={'class':'sinif'})}
    }

inlines
inlenes seçeneği ile Ebeveyn Model'e ait olan alt modelleri aynı sayfada düzenleyebilmemiz olanağını verir. Yani bir yazar eklerken aynı zamanda Yazar'a ait kitapları da ekleyebiliriz. Alanın ForeingKey olması gerekir. Elimizde şöyle iki model olduğunu düşünelim.
class Yazar(models.Model):
    isim = models.CharField(max_length=100)

class Kitap(models.Model):
    yazar = models.ForeignKey(Yazar)
    baslik = models.CharField(max_length=100)
    tarih = models.DateField()
Şimdi yazar ekleme veya düzenleme işlemi yaparken aynı zamanda yazara ait kitapları da düzenleyebilmemizi sağlayacak inlines tanımlamasını yapalım.
class KitapInline(admin.StackedInline):
    model = Kitap

class YazarAdmin(admin.ModelAdmin):
     inlines = [KitapInline]

admin.site.register(Yazar, YazarAdmin)
Burada KitapInline içerisinde hangi modeli kullanacağını belirtiyoruz. YazarAdmin içerisinde ise oluşturduğumuz inline tanımlamasını gösteriyoruz.
Inline tanımlaması yaparken admin.StackedInline'ı kullandık eğer isterseniz admin.TabularInline tanımlasını kullanabilirsiniz. Tek fark olarak Kitap modeli içerisindeki alanlar yan yana görünecektir.
list_display
Model elemanları listelenirken hangi elemanların gösterileceğini belirlememizi sağlar. Örneğin şöyle bir kullanım yapabiliriz.
class KitapAdmin(admin.ModelAdmin):
    list_display = ['baslik', 'tarih']
Kitabın yazarını almak için şöyle bir yol izleyebiliriz.
def yazari(obj):
    return  obj.yazar.isim
yazari.short_description = 'Kitap Yazarı'

class KitapAdmin(admin.ModelAdmin):
    list_display = ['baslik', yazari, 'tarih']
veya,
class KitapAdmin(admin.ModelAdmin):
    list_display = ['baslik', 'yazari', 'tarih']

    def yazari(self, obj):
        return  obj.yazar.isim
    yazari.short_description = 'Kitap Yazarı'
Eğer göstermek istediğimiz alan ForeignKey bir alan ise Model içerisindeki __unicode__() fonksiyonu çağrılır. Bizim yukarıda yaptığımız işlemi şu şekilde de yapabiliriz.
class Yazar(models.Model):
    isim = models.CharField(max_length=100)

    def __unicode__(self):
        return self.isim

class KitapAdmin(admin.ModelAdmin):
    list_display = ['baslik', 'yazar', 'tarih']
list_display ManyToManyField alanlarını desteklemez. Bu alanları göstermek için yine özel fonksiyon yazmalıyız. Önceki Kitap Yazar arasıdanki ForeignKey ilişkisini ManyToManyField haline getirelim.
def yazarlari(obj):
    return ",".join(yazar.isim for yazar in obj.yazar.all())
yazarlari.short_description="Kitap Yazarları"

class KitapAdmin(admin.ModelAdmin):
    list_display = ['baslik', yazarlari, 'tarih']
ManyToManyField alanları göstermek için ise bu şekilde bir yöntem kullanabiliriz.
Eğer alan BooleanField veya NullBooelanField ise list_display ile gösterilirken Django'nun iconları ile gösterilirler. Daha önce oluştmuş olduğumuz Yazi modeli üzerinden gösterelim.
class YaziAdmin(admin.ModelAdmin):
    list_display = ('baslik','taslak' )
Eğer isterse alanlar gösterilirken html tagleri kullanılabilir ve stil verilebilir. Örneğin kitap isimlerini renkli gösterelim.
class KitapAdmin(admin.ModelAdmin):
    list_display = ['stil_baslik', yazarlari, 'tarih']

    def stil_baslik(self,obj):
        return '%s'%obj.baslik
    stil_baslik.allow_tags = True
Herhangi bir fonksiyon yazabilir ve bu fonksiyonun dönüşüne göre Django'nun Boolean alan ikonlarını gösterebiliriz.
class KitapAdmin(admin.ModelAdmin):
    list_display = ['stil_baslik', yazarlari, 'yil']

    def yil(self,obj):
        return int(obj.tarih.strftime('%Y')) < 2000
    yil.boolean = True
Eğer kitap 2000 yılından önceye ait ise Django'nun True ikonu gösterilecektir.
Örneğin kitap yazarlarını gösterirken yazara tıkladığında düzenleme sayfasına yönlendiren bir fonksiyon yazalım. Böylece her yazar düzenleme sayfasına giden bir link bağlamış olacağız.
def yazarlari(obj):
    yazarlar=""
    for yazar in obj.yazar.all():
        yazarlar = yazarlar + ''+yazar.isim+''+" "
    return  yazarlar
yazarlari.allow_tags = True
yazarlari.short_description="Kitap Yazarları"
Burada site adını veri tabanından çekmek daha doğru olacaktır.
list_display_links
Model elemanları listelenirken değiştirme sayfasına gidilmesi için varsayılan olarak list_display ile belirtilen ilk alana link bağlanmıştır. Eğer bunun değiştirilmesi isteniyorsa kullanılır.
    list_display = ['stil_baslik', yazarlari, 'yil']
    list_display_links = ['stil_baslik',yazarlari]
Not : list_display_links içerisinde tanımlanan her alanın list_display içerisinde tanımlanmış olması gerekir.
list_editable
Model elemanları listelenirken aynı zamanda düzenlenebilme özelliği sağlar. Bu şekilde örneğin text içeriği olan bir eleman text input'una dönüşür ve eleman düzenlenebilir.
class KitapAdmin(admin.ModelAdmin):
    list_display = ['baslik', yazarlari, 'tarih']
    list_display_links = [yazarlari]
    list_editable = ['baslik','tarih']
Not : list_editable içerisinde tanımlanmış alanın list_display içerisinde tanımlanmış list_display_links içerisinde tanımlanmamış olması gerekir.
list_filter
list_filter listeleme yapılırken sağ tarafta filtreleme alanının çıkmasını sağlar. Bu alana Django'nun Kullanıcılar modelinden aşınayız zaten.
Bu şekilde bir alan çıkmasını sağlar. Örneğin daha önce oluşturmuş olduğumuz Yazi modelinde taslak alanı bulunmaktadır. Buradaki taslak alanına göre filtreleme yaptırabiliriz.
class YaziAdmin(admin.ModelAdmin):
    list_display = ('baslik','taslak', )
    list_filter = ('taslak', 'kategori', )
list_filter'a yazılan alanlar BooleanField, CharField, DateField, DateTimeField, IntegerField, ForeignKey veya ManyToManyField olabilir. Bizim yaptığımız örnekte taslak alanı BooleanField iken kategori alanı ManyToManyField. Şu şekilde görünecektir.
Filtreleme için kendi fonksiyonlarınızı yazabilirsiniz. Yazdığınız fonksiyona göre filtreleme olacaktır. Bunun için Django'nun kendi sitesinde çok güzel bir örnek bulunmaktadır. Buradan ulaşarak inceleyebilirsiniz.
list_per_page
Sayfalama yapılarak her sayfada kaç eleman gösterileceğini belirtir. Varsayılan olarak 100 gelmektedir.
class YaziAdmin(admin.ModelAdmin):
    list_per_page = 1

prepopulated_fields
slug veya başka söyleyiş ile sef_link alanı oluşturmak için kullanılır. slug kısaca herhangi bir textin tarayıcı url'ine uygun haline getirilmiş halidir. Örneğin;
Text = bu bir yazıdır.
Slug = bu-bir-yazidir
Yazi modelimizi baslik_slug alanı ekledik.
class Yazi(models.Model):
    baslik  = models.CharField(max_length=100)
    baslik_slug = models.CharField(max_length=100)
    ...
Bu özelliği kullandığımız zaman baslik alanı doldurulurken bir Javascript kodu çalışacak ve eş zamanlı olarak baslik_slug alanı otomatik olarak doldurulacaktır.
class YaziAdmin(admin.ModelAdmin):
    prepopulated_fields = {
        'baslik_slug':('baslik',)
    }

radio_fields
ForeignKey bir alan seçilirken default olarak bir seçim kutusundan seçim yapılır. Eğer bir böyle olmasını değil de seçim düğmeleri ile seçim yapılmasını istersek bu özelliği kullanabiliriz. Daha önceki Yazar,Kitap modellerini hatırlayalım.
class Yazar(models.Model):
    isim = models.CharField(max_length=100)

    def __unicode__(self):
        return self.isim

class Kitap(models.Model):
    yazar = models.ForeignKey(Yazar)
    baslik = models.CharField(max_length=100)
    tarih = models.DateField()
Şimdi buradaki Kitap içerisindeki ForeignKey için bu özelliği uygulayalım.
class KitapAdmin(admin.ModelAdmin):
    list_display = ['baslik','yazar', 'tarih']
    radio_fields = {'yazar':admin.HORIZONTAL}
Buradaki HORIZONTAL yerine VERTICAL kullanabilirsiniz. Tek fark olarak seçim düğmeleri alt alta görünecektir.
raw_id_fields
Az önce belirttiğimiz gibi seçim alanı olarak bir seçim kutusu gelir. Bu seçim kutusunu seçim düğmelerine çevirdik. Bu seçenek ise seçim yapma işlemini id üzerinden yapılmasını sağlar. Örnek üzerinden gösterelim. Daha önceki Yazı modeli içerisindeki kategori kısmını bu seçenek içerisinde kullanalım.
class YaziAdmin2(admin.ModelAdmin):
    raw_id_fields = ['kategori']
raw_id_fields ManyToManyFields ve ForeignKey alanları için kullanılabilir.
readonly_fields
Alanları sadece okunabilir yapmak için kullanılır. Buraya girilen alanlara müdahale edilemez. Örnek olarak Kitap modelinde id alanını gösterelim ama kullanıcı müdahale edemesin.
class KitapAdmin(admin.ModelAdmin):
    list_display = ['baslik','yazar', 'tarih']
    readonly_fields = ['id']


save_as
Varsayılan olarak False gelir. Bir modele eleman eklenirken veya düzenlenirken kaydet kısmında üç buton bulunur. Bunlar "Kaydet", "Kaydet ve düzenlemeye devam et" ve "Kaydet ve yenisini ekle". Eğer bu seçenek True yapılırsa var olan bir eleman düzenlenirken "Kaydet ve yenisini ekle" butonu "Yeni olarak kaydet" haline gelir. Bu butona tıklandığında ise yapılan değişiklikler yeni eleman olarak kaydedilir ve eskisi de kalır.
class KitapAdmin(admin.ModelAdmin):
    save_as = True

save_on_top
Varsayılan olarak False gelir. Bahsettiğimiz bu üç buton sayfanın altında bulunur. Eğer sayfanın üstünde de bulunması istenirse bu seçenek kullanılır.
class KitapAdmin(admin.ModelAdmin):
    save_on_top = True

search_field
Model elemanlarının listeleme sayfasına arama formu eklenmesini sağlar. Arama işlemi yapılırken search_field ile belirtilen listedeki sütunlar baz alınarak yapılır.
class KitapAdmin(admin.ModelAdmin):
    list_display = ['id','baslik','yazar', 'tarih']
    search_fields = ['baslik','yazar__isim']
Bu aramanın sorgusu şu şekildedir;
WHERE (baslik ILIKE '%dijital%' OR yazar__isim ILIKE '%dijital%')
AND (baslik ILIKE '%brown%' OR yazar__isim ILIKE '%brown%')
Sorgu içerisindeki like ifadesinde '%ifade%' kullanımı dikkatinizi çekmiştir. Eğer 'ifade%' veya '%ifade' şeklinde olmasını istersek ^ ifadesini kullanmalıyız.
class KitapAdmin(admin.ModelAdmin):
    search_fields = ['^baslik','^yazar__isim']
Sorgunun yeni hali şöyledir;
WHERE (baslik ILIKE 'dijital%' OR yazar__isim ILIKE 'dijital%')
AND (baslik ILIKE 'brown%' OR yazar__isim ILIKE 'brown%')
Eğer tam eşleşme yapmak istersek = ifadesini kullanmalıyız.
class KitapAdmin(admin.ModelAdmin):
    search_fields = ['=baslik','=yazar__isim']
Sorgunun yeni hali şöyledir;
WHERE (baslik ILIKE 'dijital' OR yazar__isim ILIKE 'dijital')
AND (baslik ILIKE 'brown' OR yazar__isim ILIKE 'brown')
Sorgulardaki ManyToManyField alanı olan yazar için sorgu bu şekilde değildir. Sadece göstermek amacı ile bu şekilde kullandık. Kaynakça

0 yorum :

Yorum Gönder