Django’da signals kullanımı

Django’da signals kullanımı
Django’nun admin paneli çoğu durum için yeterli olsa da biraz değişik bir şey yapmak istediğinizde tutorial’da bahsedilenden farklı bir işlem yapmanız gerekiyor. Sinyaller (signals), Django’nun bu durumda yardımcı olabilecek bir özelliği.

Sinyaller, uygulamada belirlenen alıcıların göndericilerin yaptığı işlemlerden haberdar olmasını sağlar. Bu ne demek biraz açalım; mesela bir modeliniz var ve bu model save edildiği zaman başka bir fonksiyon çağırmak istiyorsunuz. Modelinizi gönderici (sender) olarak belirleyip olurturduğunuz diğer bir fonksiyonu da alıcı (receiver) olarak gösterebilirsiniz.

Django’da kendiniz sinyal fonksiyonlarınızı oluşturabileceğiniz gibi hazırda bulunan sinyaller de mevcut. Modellerin kaydedilme esnasında (öncesinde ve sonrasında) tetiklenen sinyaller ve http isteği başlarken ve biterken tetiklenen sinyaller gibi.

Bir örnek vermek gerekirse:

Ekipman isimli bir modelimiz olsun. Bu modelde her değişiklik yapıldığında bunu ayrı bir tabloda tutmak istiyoruz. Böylelikle ekipmanda yapılan her değişikliği kayıt altında tutabileceğiz.

class Ekipman(models.Model):
    seri_no = models.CharField(max_length=20)
    bulundugu_yer = models.CharField(max_length=20)
    tamirde = models.BooleanField()
    notlar = models.TextField(blank=True)

Gecmis isimli bir de tablomuz var. Buna da yapılan değişiklikleri kaydedeceğiz.

class Gecmis(models.Model):
    ekipman = models.ForeignKey(Ekipman)
    seri_no = models.CharField(max_length=20)
    bulundugu_yer = models.CharField(max_length=20)
    tamirde = models.BooleanField()
    notlar = models.TextField(blank=True)

Şimdi de kayıt işlemini gerçekleştirecek fonksiyonumuzu oluşturalım.

def GecmisTut(sender, **kwargs):
    ekipman = kwargs['instance']
    gecmis = Gecmis(ekipman = ekipman, \
    seri_no = ekipman.seri_no, \
    bulundugu_yer = ekipman.bulundugu_yer, \
    tamirde = ekipman.tamirde, \
    notlar = ekipman.notlar)
 
    ekipman.save()

Alıcı fonksiyonu her zaman sender ve kwargs parametrelerini almalıdır. sender parametresi göndericinin sınıfını (instance’ını değil) tutar. Oluşturduğumuz modelin kendisini (instance’ını) almak için kwargs içindeki instance anahtarını kullanmamız gerekir.

Bu fonksiyonu iki şekilde gönderici ile bağlayabiliriz. Model kaydedildikten sonra alıcının çağırılmasını istediğimize göre:

from django.models.signals import post_save

def GecmisTut(sender, **kwargs):
    ekipman = kwargs['instance']
    gecmis = Gecmis(ekipman = ekipman, \
    seri_no = ekipman.seri_no, \
    bulundugu_yer = ekipman.bulundugu_yer, \
    tamirde = ekipman.tamirde, \
    notlar = ekipman.notlar)
 
    ekipman.save()
 
post_save.connect(GecmisTut, sender=Gecmis)

Veya fonksiyonu bir decorator ile kullanalım:

from django.models.signals import post_save

@receiver(post_save, sender=Ekipman)
def GecmisTut(sender, **kwargs):
    ekipman = kwargs['instance']
    gecmis = Gecmis(ekipman = ekipman, \
    seri_no = ekipman.seri_no, \
    bulundugu_yer = ekipman.bulundugu_yer, \
    tamirde = ekipman.tamirde, \
    notlar = ekipman.notlar)
 
    ekipman.save()

şeklinde kullanabiliriz.

Flask Rehberi

Kurulum

Flask’ı kurmak için pip kullanmanızı tavsiye ederim. Aşağıdaki komutu kullanabilirsiniz:
pip install flask
Diğer paket kurulumlarında olduğu gibi bunu da virtualenv ile kullanmanız şart olmasa da oldukça gerekli.

Basit Bir Flask Uygulaması

Basit bir Flask uygulaması aşağıdaki gibi olabilir:

from flask import Flask
app = Flask(__name__)

@app.route('/')
def merhaba():
    return 'Merhaba dunya!'

if __name__ == '__main__':
    app.run()

Bu kadar kod web sunucusunu çalıştırmaya ve bir satır yazı döndürmeye yeterli. Dosyayı merhaba.py olarak kaydedin. Sunucuyu çalıştırmak için:

python merhaba.py

Running on http://127.0.0.1:5000/ şeklinde bir satır dönmesi gerekiyor. Bu sayfayı ziyaret ederek “Merhaba dunya!” yazısını görebilmelisiniz. Kodlarda her değişiklik yaptığınızda sunucuyu durdurup tekrar başlatmak yerine
app.run(debug=True) ile sunucuyu başlatabilirsiniz. Elbette debug seçeneğini gerçek sunucunuzda asla kullanmamanız gerekiyor.

Routing

Flask’ta route için decorator’lar kullanılıyor.

@app.route('/')
@app.route('/anasayfa')
def anasayfa():
    return 'Ana sayfa'

şeklindeki bir örnekte kök dizine (yukarıdaki adres) ve index sayfasına ziyaretler aynı fonksiyonu (anasayfa) çağıracaktır.
Adreslere değişken eklemek için aşağıdaki yol izlenebilir:

@app.route('/kullanici/<kullaniciadi>')
def welcome(kullaniciadi):
    return 'Merhaba %s' % kullaniciadi

Tarayıcınıza /kullanici/bora yazdığınızda “Merhaba bora” yazısını döndürecektir.

URL Yapılandırma

Uygulamanızda tanımladığınız url’leri uygulamanızda başka yerlerde kullanırken olduğu gibi yazmak uygulamanın genişletilebilirliği ve yönetilebilirliği açısından ileride zorluk çıkmasına neden olabilir. Tanımladığınız url’de değişiklik yaptığınız zaman bütün kodda gerekli yerleri bulup düzeltmeniz gerekecektir. Bunun yerine bunları üreterek tek bir yerden yönetimini sağlayabilirsiniz.

from flask import Flask, url_for, redirect
app = Flask(__name__)

@app.route('/kullanici')
def kullanici():
    return 'Kullanici sayfasi'

@app.route('/yonetici')
def yonetici():
    return 'Yonetici sayfasi'

@app.route('/giris/<kullanici>')
def giris(kullanici):
    if kullanici == 'yonetici':
        return redirect(url_for('yonetici'))
    else:
        return redirect(url_for('kullanici'))

if __name__ == '__main__':
    app.run(debug=True)

Yukardaki kodu bir dosyaya kaydedip çalıştırırsanız, tarayıcıda /giris/yonetici adresini ziyaret ettiğinizde yönetici sayfasını, diğer herhangi bir kullanıcı adı (/giris/kullanici veya /giris/bora) ile ziyaret ettiğinizde kullanıcı sayfasını göreceksiniz. Bu işlemin faydası, daha sonra bir nedenle adresi değiştirmek istediğinizde diğer kullandığınız yerlerde değişiklik yapmaya gerek kalmaması.

HTTP Metotları

Varsayılan olarak route’lar GET metotunu destekler. Form göndermek istediğinizde POST metotunu da desteklenen metotlar arasına eklemeniz gerekebilir.

@app.route('/giris', methods=['GET', 'POST'])
def giris():
    if request.method == 'POST':
        #login işlemleri
    else:
        #form göster

Statik Dosyalar

Css ve javascript dosyaları gibi statik dosyaları kullanabilmek için uygulama klasörünüzde static isimli bir klasör yaratmanız gerekir. Bu statik dosyalara aşağıdaki şekilde url yaratabilirsiniz:

url_for('static', filename='style.css')

Template Renderlama

Flask varsayılan olarak Jinja2 template sistemini kullanır. Htmldosyalarını render etmek için render_template metotunden yararlanılır. Yarattığınız html dosyalarını uygulama klasörü içinde template klasörü altında saklayabilirsiniz.

from flask import render_template

@app.route('/giris/<ad>')
def giris(ad):
    return render_template('giris.html', ad=ad)

Çağırılan html sayfası da aşağıdaki şekilde olabilir:

<h1>Merhaba {{ ad }}</h1>
Jinja2 template sistemi hakkında daha fazla bilgi için sitesini ziyaret edebilirsiniz.

Django’da Admin panelinde modelleri Türkçe çoğul yapma

Django’da admin panelinde modeller verdiğiniz ad ne olursa olsun çoğul yapılırken sonuna ‘s’ takısı alır (örneğin Personels). Oysa ki biz Personel modelini admin panelinde Personeller olarak görmek isteyebiliriz.

Bu durumda Personel modeline aşağıdaki sınıfı eklemeniz yeterli olacaktır:

class Personel(models.Model):
    .......
    class Meta:
        verbose_name_plural = "Personeller"

Python ile listeleri sıralamak

Python’da listeleri sıralamak için iki yol izlenebilir. Birincisi sort() metodu. Bu metod üzerinde uygulanan listeyi sıralar. Ancak none döndürdüğüne dikkat edilmelidir.

liste = [6,3,9]
liste.sort()
liste
[3, 6, 9]

Yani aşağıdaki şekilde bir atama yapamazsınız.

sirali_liste = liste.sort()
Bunun yerine sorted fonksiyonunu kullanabilirsiniz.

sirali_liste = sorted(liste)
Sorted fonksiyonu ayrıca argümanlarla özelleştirilebilir.

liste = ["google", "android", "java"]
sorted(liste, reverse=True)
['java', 'google', 'android']

Bence sorted fonksiyonunun en güzel tarafı, listeleri sıralamak için custom fonksiyon yazılabilmesidir.

sorted(liste, key=len)
['java', 'google', 'android']

Len gibi hazır bir fonksiyon kullanılabilir veya yazılan bir fonksiyon kullanılabilir.

def Fonk(a):
    return a(-1)
sorted(liste, key=Fonk) ['java', 'android', 'google']

Django’da QuerySet’ler 2 – Filtreleme

Queryset’lerde alınan veri üzerinde filtreleme işlemi aşağıdaki örnekte olduğu gibi yapılabilir.

kisiler = Kisi.objects.all()
kisiler.filter(isim='Ahmet')


Zincirleme filtre de aşağıdaki gibidir.

kisiler = kisiler.filter(yas=20)
kisiler = kisiler.filter(isim__startswith='A')


Dikkat edilmesi gereken nokta bu query’ler çalıştırıldığında veritabanı ile herhangi bir bağlantı kurulmamasıdır (şu ana kadar sadece veriyi çekecek olan query’i oluşturduğumuzu düşünebilirsiniz). Bağlantı queryset’ler içerisindeki bilgiye ulaşılmaya çalışıldığında kurulur.

kisiler = Kisi.objects.filter(isim__startswith='A')
kisiler = kisiler.filter(soyad__contains='E')
for kisi in kisiler:
print kisi.isim


Yukarıdaki kod çalıştırıldığında belirtilen filtreler ile getirilen sonuçları ekrana yazar. Bu sırada veritabanı ile sadece bir kere bağlantı kurulur.

Django’da QuerySet’ler 1

Django’da QuerySetler

Django’da queryset’ler çağırıldığında cache’de tutulurlar. Böylece çağırılan queryset tekrar kullanılarak database üzerindeki yük azaltılmış olur.

Aşağıdaki query’ler çalıştırıldıklarında veritabanı ile iki kere bağlantı kurarak toplamda iki ayrı sorgu çalıştırır.

q = kisi.isim for kisi in Kisi.objects.all()
q = kisi.yas for kisi in Kisi.objects.all()


Bunun yerine daha az kaynak tüketen aşağıdaki yol izlenebilir.

kisiler = Kisi.objects.all()
kisi.isim for kisi in kisiler
kisi.yas for kisi in kisiler

Bu şekilde veritabanı ile sadece bir defa bağlantı kurularak queryset cache’e alınır.

Google Python Class string2.py Çözümü

Google’ın Python alıştırmaları veren sınıfında ödevlerden biri olan string2.py’nin front_back alıştırmasını aşağıdaki şekilde çözdüm. Bu alıştırmada verilen iki string’in uzunluklarına göre bölünmesi ve tek bir string haline getirilmesi isteniyor. Eğer string uzunluğu çift ise ortadan ikiye bölünecek, tekse yine ortadan ikiye bölünecek ancak artan (yani string’in ortasındaki) harf baştaki parçaya eklenecek. Bundan sonra istenen ise ilk string’i a1 ve a2 olarak, ikincisini de b1 ve b2 olarak bölmüşsek hepsinin a1+b1+a2+b2 şeklinde tek string halinde döndürülmesi.

def front_back(a, b):
a1 = a[:len(a)/2+len(a)%2]
a2 = a[len(a)/2+len(a)%2:]
b1 = b[:len(b)/2+len(b)%2]
b2 = b[len(b)/2+len(b)%2:]
print a1,a2,b1,b2
return a1+b1+a2+b2

Python’da liste kopyalama

Python’da listeleri aşağıdaki eşitlikle kopyalamaya çalıştığınızda liste içerisindeki değerler kopyalanmaz; listenin referans değeri kopyalanır. Bu durumda bir listede yapılan değişiklikler diğerinde de geçerli olur.

liste1=["deger1","deger2","deger3"]
liste2=liste1
liste2.append("deger4")
print liste1,liste2
['deger1', 'deger2', 'deger3', 'deger4'] ['deger1', 'deger2', 'deger3', 'deger4']

Bunun yerine değerleri kopyalamak için aşağıdakiler kullanılabilir:

liste1=["deger1","deger2","deger3"]
liste2=list(liste1)
liste2.append("deger4")
print liste1,liste2
['deger1', 'deger2', 'deger3'] ['deger1', 'deger2', 'deger3', 'deger4']

Veya:
liste1=["deger1","deger2","deger3"]
liste2=liste1[:]
liste2.append("deger4")
print liste1,liste2
['deger1', 'deger2', 'deger3'] ['deger1', 'deger2', 'deger3', 'deger4']

Son iki örnekte verilen kodlarda sadece değerler kopyalanır; bir listede yapılan değişiklik diğerini etkilemez.

Python’da Türkçe karakter sorunu

Windows ortamında Python kullanırken Türkçe karakterler hata alınmasına sebep olur. Bunun önüne geçebilmek için kodun başına #-*- coding: cp1254 -*- yazabilirsiniz. Linux kullanıcıları ise #-*- coding: utf-8 -*- satırını kullanabilir.

Google App Engine – Python 2.7 ile WSGI arayüzü

Google App Engine ile uygulama geliştirirken app.yaml ile uğraşılmak istenilmezse Python 2.7 kullanarak WSGI interface’i ile app içine gerekli tanımlar yapılabilir.

import webapp2
class MainPage(webapp2.RequestHandler):
def get(self):
self.response.headers['Content-Type'] = 'text/plain'
self.response.out.write('Hello, webapp World!')
class MyPage(webapp2.RequestHandler):
def get(self):
#Stuff to do
app = webapp2.WSGIApplication([('/', MainPage),
('/mypage',MyPage)],
debug=True)