16 Kasım 2013 Cumartesi

Python Metot Türleri @classmethod @staticmethod


Python sınıflarında, temel olarak 3 farklı şekilde metot tanımı yapılabilir.
  • Örnek (Instance) metotları
  • Statik (@staticmethod) metotlar
  • Sınıf (@classmethod) metotları
Bunların arasındaki başlıca fark, aldıkları argümanlardır. Örnek metotlarına, ilk argüman olarak, objenin kendisine bir referans gönderilir. Bu argümana, geleneksel olarak self adı verilir. Statik metotlar, kendisini çağıran sınıf veya örnek hakkında herhangi bir bilgiye sahip değildir. Bunlar, işlevini kaybetmeden, sınıf dışında da aynı şekilde tanımlanabilir. Sınıf metotları ise, otomatik olarak, kendisini çağıran sınıfa veya örneğin sınıfına bir referans alır. Bu argümana da geleneksel olarak cls adı verilir.

Örnek (Instance) Metotları

 

Python'da yeni bir sınıf tanımlandığınız, yazdığınız metotlar öntanımlı olarak, örnek (instance) metotlarıdır. Örnek metotları, ilk argüman olarak, kendisini çağıran örneğe bir referans alır. Böylece, geçerli örneğin niteliklerine erişim ve müdahale imkanı bulurlar. Aşağıdaki kodları inceleyelim;

 

class ikisayi(object):

    def __init__(self, a, b):
        self.a = a
        self.b = b

    def toplam(self):
        return self.a + self.b


x = ikisayi(3,5)
print(x.toplam) # 1
print(ikisayi.toplam) # 2
print(x.toplam()) # 3
print(ikisayi.toplam(x)) # 4 
 
Programın çıktısı:
#1: <bound method ikisayi.toplam of <__main__.ikisayi object at 0x021F0390>> #2: <unbound method ikisayi.toplam> #3: 8 #4: 8 Yukarıda, görmeye alışık olduğunuz türden bir sınıf tanımımız var. Bu sınıf, iki tane sayı hakkında veri tutmak için kullanılıyor. Yine, bolca karşınıza çıkacağı gibi __init__ başlangıç metodu var. toplam metodu ise, normal şekilde tanımlanmış, yani bir örnek (instance) metodu. toplam metodumuz, her örnek metodunda olduğu gibi, zorunlu bir ilk argüman alıyor. Bu argümana, çalışma anında oluşturulmuş bir ikisayı objesi geçirilecek. Böylece, toplam metodu, hangi objenin a niteliğiyle b niteliğini toplaması gerektiğini bilebilecek.
Program çıktısına dikkat edersek, x.toplam metodunun bound method olduğunu görüyoruz.Yani, ikisayi.toplam metodu belirtilen adresdeki ikisayi objesine bağlanmış. Yani, x.toplam metodu çağırıldığında, ikisayi.toplam metodu, ilk argüman olarak, o adresdeki objeyi alacak.
Diğer yandan, aynı metoda, sınıf üzerinde eriştiğimizde, bir unbound method buluyoruz.Yani, bu method, herhangi bir örneğe bağlanmamış. Dolayısıyla, otomatik bir ilk argüman almayacak. Eğer ikisayi.toplam() şeklinde çağırırsanız, aşağıdaki hatayı alırsınız:
TypeError: unbound method toplam() must be called with ikisayi instance as first argument
#3 ve #4 numaralı ifadeler aynı çıktıyı veriyor, çünkü, teknik olarak aynı şeyler. Siz, x.toplam() çağırdığınızda, Python bunu kendi içinde ikisayi.toplam(x) şekline dönüştürüyor.

Statik Metotlar

Statik Metotlar, kendisini hangi sınıf veya örneğin çağırdığını bilmez. Sadece kendine verilen argümanları bilir, örnek veya sınıf metotları gibi, gizli bir ilk argüman almazlar. Bu yönden bakıldığında, bu fonksiyonun sınıf içinde yazılmasıyla, sınıf dışında yazılması arasında, hiçbir fark yoktur. Ancak, aynı modül içerisindeki birçok fonksiyonu anlamsal bütünler içinde toplamak gerektiğinde kullanılabilir. Bunları tanımlamak için, metot tanımından önce @staticmethod dekoratörü kullanılır.

Sınıf Metotları

Diğer yandan, sınıf metotları, ilk argüman olarak, kendisini çağıran sınıfa veya kendisini çağıran örneğin sınıfına mecburi/otomatik bir referans alır. Bunu o sınıfın bir örneğini oluşturmak istediğiniz durumlarda (bkz: Factory) kullanabilirsiniz. Kendisini çağıran sınıfa bir referans aldığı için, alt sınıflarda da istenildiği gibi çalışacaktır. Örneğin, diyelim ki, bir telefon numarasını temsil eden bir sınıfınız var:
class TelNo(object):

    def __init__(self, ulkekodu, alankodu, numara):
        self.ulkekodu = ulkekodu
        self.alankodu = alankodu
        self.numara = numara
Burada, bir telefon numarasının, ülke kodu, alan kodu ve numrasını ayrı değişkenlerde tutan bir sınıf tanımladık. Muhtelemen, elimizdeki telefon numaraları, bir dosya veya veritabanında "+90 507 7997272" gibi karakter dizileri içerisinde tutuluyordur. Dolayısıyla, böyle karakter dizilerinden TelNo objesi üreten bir fonksiyona ihtiyacımız olacak.
def str_to_telno(string):
    return TelNo(*string.split(" "))

mytel = str_to_telno("+90 507 7997272")
str_to_telno fonksiyonu, verilen karakter dizisini boşluklarından bölüp, bunlardan yeni bir TelNo objesi oluşturuyor. Telefon numarası tutabilmek güzel ama, bu numarayı arayabilmemiz daha da güzel olurdu. Bunun için, TelNo'nun aranabilen bir alt sınıfını oluşturalım.
class AranabilenTelNo(TelNo):

    def ara(self):
        "bu numaraya VOIP üzerinden bağlantı kur"
        pass # gerçekten bu fonksiyonu kodlamamı beklemiyordunuz umarım :)

def str_to_aranabilentelno(string):
    return AranabilenTelNo(*string.split(" "))
Aynı TelNo'da yaptığımız gibi, bir karakter dizisinden AranabilenTelNo oluşturan bir fonksiyon da tanımladık.
Bu yöntemin şöyle bir sıkıntısı var. Diyelim ki, ileride, mesaj atılabilen telno, mms gönderilebilen tel no vs. gibi bir çok alt sınıf tanımlayacaksınız. Her seferinde tek tek bu dönüşüm işlemini yapmak hem yorucu olacak, hem de kodlar çorba olacak.
Buna alternatif olarak, TelNo objesinde, from_string isminde bir @classmethod tanımladığımızda, ne olduğuna bakalım;
class TelNo(object):
    def __init__(self, ulkekodu, alankodu, numara):
        self.ulkekodu = ulkekodu
        self.alankodu = alankodu
        self.numara = numara

    @classmethod
    def from_string(cls, string):
        return cls(*string.split(" "))

class AranabilenTelNo(TelNo):
    def ara(self):
        pass

mytel = TelNo.from_string("+90 507 7997272")
print(type(mytel)) # 1
myaranabilentel = AranabilenTelNo.from_string("+90 507 7997272")
print(type(myaranabilentel)) # 2
Program Çıktısı:
#1: <class '__main__.TelNo'> #2: <class '__main__.AranabilenTelNo'> Program çıktısından görebileceğiniz gibi, iki sınıfın from_string metotları, doğru sınıfa ait objeler oluşturuyor. Böylece, hem alt sınıf için tekrar ayrı fonksiyon yazmak zorunda kalmadık, hem de, doğru sınıfa ait bir örnek oluşturabildik.
Dikkat ettiyseniz, @classmethod ile tanımladığımız metotları, doğrudan sınıf üzerinden çağırdık. Halbuki, Örnek (Instance) metotlarını, sınıfın bir örneği üzerinden çağırıyorduk. Özetle, sınıfın kendisi ile ilgili işlem yapılacak durumlarda, @classmethod kullanabiliriz.s
 

 

 

 

10 Kasım 2013 Pazar

Tornado websocket client

Tornado'nun sunucu hizmetleri ile ilgili bir sürü örnek var ama istemci olarak hele hele WebSocket için neredeyse hiçbir örnek bulamadım internette, onun için buraya yazalım dursun, bir gün birine lazım olur.

from tornado import websocket, ioloop, gen, escape

class SocketClient():
    def __init__(self):
        self.headers = httputil.HTTPHeaders()
        self.headers['Content-Type'] = 'application/json'
        self.request = httpclient.HTTPRequest(
            url = "wss://some.url",
            validate_cert = False,
            client_key = "client-key.pem",
            client_cert = "client-cert.pem",
            connect_timeout = 60,
            request_timeout = 60)


    def connect(self, connCallback, msgCallback):
        self.request.headers = self.headers
        self.__msgcallback = msgCallback
        self.__conncallback = connCallback
        wssConn = websocket.WebSocketClientConnection(ioloop.IOLoop.current(), self.request)
        wssConn.connect_future.add_done_callback(self.__on_connResult)




    def __on_connResult(self, conn):
        if conn.exception() == None:
            self.__wssConn = conn.result()
            self.__conncallback(True)
            self.__read()
        else:
            print(conn.exception().args)
            self.__conncallback(False)




    def send(self, data):
        self.__wssConn.write_message(escape.utf8(json.dumps(data)))




    @gen.coroutine
    def __read(self):
        while True:
            msg = yield self.__wssConn.read_message()
            if msg is None:
                self.__conncallback(False)
                break
            self.__msgcallback(msg)

Python ile socket programlamaya giriş.

Merhabalar,
Bu yazıda, Python programlama diliyle soketlerin kullanılışı konusuna kısaca giriş yapmaya çalışacağım. Okuyucuda soketler hakkında temel bilgilerin olduğunu varsayıyorum. Bunların ne olduğu veya ne için kullanıldığı konusunda hiçbir bilgisi olmayanlar için şöyle özetleyebiliriz; soketler iletişim kanallarıdır. Bunlar aynı bilgisayarda iki işlem arası iletişim sağlayabilse de (örn: unix soketleri) bunları en çok ağ üzerinde iletişim için kullanırız. İnternetten bilgisayarınıza gelip giden tüm veriler için, mutlaka birer soket kullanılır.
Anlatıma geçmeden önce şunu da söylemek istiyorum ki, Python'daki socket modülünü lazım olmadıkça kullanmayınız. Demek istediğim şu ki, bir internet sayfası indirmek için socket açmak, HTTP başlıklarını göndermek, gerekirse yönlendirilen sayfaya yeniden soket açmak gibi bir uğraşa girmeyin. Python bu tip yaygın kullanımlar için zaten daha üst seviye modüllere sahip bir dil. Amerika'yı yeniden keşfetmeye gerek yok.
Öyleyse, Python'daki socket modülünü alternatif bulamadığımız durumlarda kullanalım. Ağ bakımı/programcılığı konusunda bu modülün kullanım alanı kendisini belli edecektir.
Bir örnekle başlayalım;
s = socket.socket(
  socket.AF_INET, socket.SOCK_STREAM)

s.connect(("www.google.com.tr", 80))
Bu örnekte bir client (ingilizcede müşteri demek) soket oluşturduk. Bunun anlamı, bu soket kendisi veri sunmayacak, bir sunucuya bağlanacak demek. Sunucu programlara ileriki yazılarda göz atmaya çalışacağım.
Soket oluşturma fonksiyonuna ilk verdiğimiz argüman, bu soketin adresleme şeklini gösteriyor diyebiliriz. Bunun socket.AF_INET olması, bunun bildiğimiz IP adresi soketi olduğunu gösteriyor. Bunun yerine, socket.AF_INET6 ile IPv6 kullanabiliriz. Bir de socket.AF_UNIX var ki, bu UNIX soketleri için kullanılıyor. Dolayısıyla her sistemde bu sabit tanımlı olmayabilir.
İkinci argüman ise bu soketin iletişim tipini gösteriyor. socket.SOCK_STREAM, en yaygın kullanılan TCP iletişim tipidir. Bundan sonra en yaygın kullanılan iletişim tipi UDP için socket.SOCK_DGRAM sabiti kullanılır.
Daha sonra, elimizdeki s isimli soket objesinin connect isimli metoduyla, soketimizi internetteki bir diğer sokete (yani sunucuya) bağlayabiliriz. Bu metot adres türüne göre farklı argümanlar alabilir. socket.AF_INET için, adres ve porttan oluşan bir tuple veri tipi alır.
Eğer internetle veya işletim sistemiyle ilgili bir hata oluşmadı ise, şu andan sonra soketimiz yazmak ve okumak için hazır. Bu işlemler için sırasıyla send ve recv metotları kullanılır. Aynı örnekten devam ederek, şunu deneyebiliriz;
s.send("GET / HTTP/1.1\r\nHost: www.google.com.tr\r\nConnection: Close\r\n")
while True:
    msg = s.recv(512) # 512 byte veri okumaya çalış
    if not msg: # Eğer boş döndüyse,
        break
    print msg
Not: Kodlar içerisindeki adres html tagları içinde görünüyorsa, onları kaldırın. Galiba Tumblr otomatik olarak onu linke çevirmeye çalışıyor :/
Burada, muhtemelen yapılabilecek en kısa HTTP isteğini gerçekleştirdik. Daha sonra da, 512 byte'lar halinde okuyabildiğimiz kadar veri okuduk. İnternetten gelecek verinin tümünün ne kadar olduğunu bilmediğimiz için, boş veri okuyana kadar okumaya devam etmemiz gerekiyor. Bir yandan okudukça, bir yandan da ekrana yazmaya devam ettik.
Ekleme: Connection: Close header'ını eklemek önemli. Bu header olmazsa, server yeni bir istek almak için bağlantıyı açık tutabilir. Bu durumda, program bağlantının kapanacağını varsaydığından, bağlantının iki ucu da diğerinden veri beklerken program donabilir. (bkz: HTTP persistent connection)
Artık s isimli soketimizle işimiz bitti. HTTP protokolünde, bir kez veri okuduktan sonra bağlantı kapanır. Yeni bir sayfa okumak istersek, sıfırdan bir soket bağlantısı gerçekleştirmemiz gerekir.
Bu yazıda konuya genel bir giriş yaptım. Bir sonraki yazıda sunucu programlarda soketlerin nasıl kullanılacağına değinmeyi planlıyorum.

Python Modüllerinde __name__ Özelliğinin Kullanımı

Modüller yenilir mi, içilir mi?

Öncelikle python’da modüllerden ufak bi’ bahsedeyim. Bildiğiniz gibi bir betik içersinde kodlarımızı tekrar tekrar yazmamak için bir kez fonksiyon tanımlamamız yeterli. O fonksiyonu kullanarak kodları tekrar kullanabiliyoruz. Peki ya aynı fonksiyona başka bir Python programında da ihtiyacımız olursa ne yapacağız? Cevap tabii ki modül kullanmak.
Python’da modül yazmak için birçok metod var ama bunlardan en basit ve en yaygın olanı fonksiyonlar ve değişkenler için .py uzantılı dosyaları kullanmak. Yani evet, şimdiye kadar yazdığınız her python betiği aynı zamanda birer modül. Diğer bir modül oluşturma yöntemi de C veya C++ programlama dilleri ile yazmaktır.
Yani sonuç olarak python’da modül dediğimiz şey aslında bizim yazdığımız betiklerdir. Yani yazdığımız her python betiği bir modül olarak başka bir betikte içe aktarılabilir(import). Modüller için ayrıca buraya bakabilirsiniz.

Bir modül’ün ismi

Yavaş yavaş asıl değinmek istediğim konuya geleyim. Python’da her modülün bir ismi vardır. Python yorumlayıcısı bir modülü yorumlarken o modüldeki tüm kodları çalıştırmadan önce bazı değişkenler tanımlar. Bunlardan birisi __name__ değişkenidir. Bu değişken, o modül’ün çalıştırılma şekline göre başka değerler alır.
Eğer python yorumlayıcısı bizim yazdığımız modülü ana program olarak çalıştırıyorsa __name__ değişkeni '__main__' değerini alır. Ancak yazmış olduğumuz modül başka bir modül içinden çağırılıyorsa bu sefer __name__ değişkeni kendi modül’ünün adını alır.
Anlaşılması için bunları bir örnekle açıklayayım;
Örneğin ornek_modul.py isminde bir dosyaya kaydedeceğimiz şöyle bir betik yazalım:
__name__ özelliğinin kullanımı
1
2
3
4
if __name__ == '__main__':
    print('Kendi dosyam tarafından çalıştırıldım.')
else:
    print('Başka bir modül tarafından içe aktarılarak çalıştırıldım.')
Şimdi bu betikle ilgili çıktılara bakalım:
çıktı testleri
1
2
3
4
5
6
$ python3 ornek_modul.py
Kendi dosyam tarafından çalıştırıldım.
$ python3
>>> import ornek_modul
Başka bir modül tarafından içe aktarılarak çalıştırıldım.
>>>
Görüldüğü gibi ornek_modul.py doyasını kendi başına çalıştırısak __name__ değişkeni '__main__' değerini alıyor. Fakat python’un etkileşimli kabuğuna girip ornek_modul modülünü içeri aktarırsak 1. koddaki else bloğuna düşeriz çünkü __name__ değişkeni bu defa modülün ismi olan ornek_modul değerini almış olur.

7 Kasım 2013 Perşembe

django web framework kullanmak için rasyonel nedenler .


Merhaba arkadaşlar ben yasin aktimur, ortalama 6 senedir internet üzerinde farklı projeler geliştiriyorum ve gelirimin tamamını  bu projelerindeki ürünleri satarak elde ediyorum.
ilk projelerimi herkes gibi php ile yazıp shared hosting üzerinde paylaşıyordum.
Geliştirdiğim projelerin kullanıcı sayısı olması gerektiğinden çok daha fazla sayılara eriştiriğinde profesyonel bir tercih yapmam gerekti ve şimdi o tercihi yaparken neden django’yu seçtiğimi sizlerle paylaşacağım.
Php ile geliştirilen büyük web siteleri : Wikipedia ve facebook
Wikipedia : 2001 senesinde yazılmış bir internet projesi ve şu anda var olan teknolojilerin hiç birisini kullanmıyor o yüzden 2013 yılında php için  referans gösterilmesi aslında gereksiz.
Facebook : 2004 senesinde kurulduğunda markın farklı bir seçeneği yoktu fakat sonradan friendfeed’e 15 milyon $ nakit ve 32.5  milyon dolar facebook hissesini babasının hayrına vermedi bu kadar para bayılmasının nedeni aslında Python ile yazılmış alt yapısını facebook’a adapte etmekti ve ettide zaten o alt yapıyıda açık kaynak olarak paylaştı. Bu arada yazılımın ismi : tornado web server . Django gibi geniş çaplı değil sadece c10k problemine çözüm getiriyor realtime sistemler için ayrıca django ile adaptasyon sağlayabiliyor yani tek başına tornado bi işe yaramıyor denebilir.
Ruby ile geliştirilen servisler : Twitter , Hulu
Ruby tercih etmeme nedenimde tornadonun python  ile yazılmış olup django ile senkrinizasyonunun yapılabilmesi realtime bir sistem kuracaksanız node.js şart lakin twitter.com ‘un mesajlaşma sistemindeki rezaleti ruby’deyki realtime eksikliğini açıkça gözler önünede seriyor.

4 Kasım 2013 Pazartesi

Paypal ipn with django

Is this your first time integrating Paypal with Django? Can't get the signals to work? Don't know how to test django-paypal on your local machine? Hopefully, this step-by-step blog post will address all the above mentioned problems and more. This tutorial will cover only Paypal Standard IPN although most of the initial steps are applicable for the other payments methods as well.

1. Install django-paypal

There are a couple of version floating around namely: 
Cramer's is a fork from Boxall's version so it's more update. For this tutorial, we will be using Boxall because we found Boxall's first.
  1. git clone git://github.com/johnboxall/django-paypal.git paypal

2. Create a PayPal developer account

  1. Goto https://developer.paypal.com and sign up.
  2. Once you're in, create a preconfigured account
  3. Do this twice, one for account type Buyer and one for account type Seller
  4. Your dashboard should then look like this:

3. Modify settings.py file

  1. For the PAYPAL_RECEIVER_EMAIL, set this to the business account email you created in step 2. In this example, it is naiyun_1311845021_biz@od-eon.com
  2. After you have entered this, run syncdb to create the paypal tables
  1. # settings.py
  2. ...
  3. INSTALLED_APPS = (... 'paypal.standard.ipn', ...)
  4. ...
  5. PAYPAL_RECEIVER_EMAIL = "naiyun_1311845021_biz@od-eon.com" #change this to yours

4. Create a url

# urls.py

  1. from django.conf.urls.defaults import patterns, include, url
  2. urlpatterns = patterns('',
  3. # Examples:
  4. url(r'^$', 'cat.homepage.views.home', name='home'),
  5. url(r'^paypal/$', 'cat.homepage.views.paypal', name='paypal'),
  6. url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
  7. url(r'^admin/', include(admin.site.urls)),
  8. )
  9. urlpatterns += patterns('',
  10. (r'^something/hard/to/guess/', include('paypal.standard.ipn.urls')),
  11. )
  1. Replace cat.homepage with your project.appname

5. Create a view and template

  1. # views.py
  2. ...
  3. from paypal.standard.forms import PayPalPaymentsForm
  4. from django.conf import settings
  5. from django.core.urlresolvers import reverse
  6. def paypal(request):
  7. # What you want the button to do.
  8. paypal_dict = {
  9. "business": settings.PAYPAL_RECEIVER_EMAIL,
  10. "amount": "1.00",
  11. "item_name": "name of the item",
  12. "invoice": "unique-invoice-id",
  13. "notify_url": "%s%s" % (settings.SITE_NAME, reverse('paypal-ipn')),
  14. "return_url": "http://www.example.com/your-return-location/"
  15. "cancel_return": "http://www.example.com/your-cancel-location/",
  16. }
  17. # Create the instance.
  18. form = PayPalPaymentsForm(initial=paypal_dict)
  19. context = {"form": form.sandbox()}
  20. return render_to_response("paypal.html", context)
  21. # paypal.html
  22. {{ form }}
  1. Don't worry about what to put for settings.SITE_NAME first, we'll get to that in a bit
  2. The notify_url is the url that PayPal will try to send a POST request
  3. return_url is the page that Paypal redirects you to when the transaction is complete
  4. The form key in context renders a PayPal button. Right now we're using form.sandbox() to initiate the test process. Change this to form.render() to send the user to the real PayPal site
  5. You can add a "custom" key and value to the paypal_dict if you want to add a custom field later on
  6. If you're doing this test multiple times, you might run into an error message given by Paypal that says something like: This invoice has already been paid. For more information, please contact the merchant. To get around this, just change the invoice value everytime you try to make a purchase.

6. Now we fix the SITE_NAME

Sign up for an account at www.dyndns.com
  1. Add a new hostname. For service type, choose Host with IP address. Next, click on the link to populate the IP Address
  2. You should have something that looks like this:
  3. Add the Hostname to your settings.py file as SITE_NAME like so
  1. # settings.py
  2. ...
  3. SITE_NAME = 'http://nai.dyndns-free.com'
  4. ...

7. Run Django development server on your local IP

  1. For this to work, you need to configure your router to open port 80. The normal process is roughly like this:
    1. Goto your router IP e.g. 192.168.1.1
    2. Look for firewall or port forwarding
    3. Create an exception called 'django' or whatever with port 80 open
    4. Add this exception to the list of allowed applications that is tied to your computer
    5. Save
  2. Now, run Django's development server using sudo /etc/init.d/apache2 stop; sudo ./manage.py runserver 192.168.2.102:80
  3. Change 192.168.2.102:80 to your own IP address which you can locate by running ifconfig in bash. Retain port 80
  4. If all goes well, you should be able to open up in your browser the hostname you created at dyndns. In my case, nai.dyndns-free.com/paypal looks like this:

8. What goes on under the hood?

When someone uses this button to buy something PayPal makes a HTTP POST to your "notify_url" which comes as part of the Paypal package. PayPal calls this Instant Payment Notification (IPN). The view paypal.standard.ipn.views.ipn handles IPN processing. We have already set the notify_url in step 4 as: urlpatterns += patterns('',(r'^something/hard/to/guess/', include('paypal.standard.ipn.urls')),)

When the notify_url is called, the ipn view is executed as shown below. You will need to include @csrf_exempt in the view as well

  1. @require_POST
  2. @csrf_exempt
  3. def ipn(request, item_check_callable=None):
  4. """
  5. PayPal IPN endpoint (notify_url).
  6. Used by both PayPal Payments Pro and Payments Standard to confirm transactions.
  7. http://tinyurl.com/d9vu9d
  8. PayPal IPN Simulator:
  9. https://developer.paypal.com/cgi-bin/devscr?cmd=_ipn-link-session
  10. """
  11. flag = None
  12. ipn_obj = None
  13. form = PayPalIPNForm(request.POST)
  14. if form.is_valid():
  15. try:
  16. ipn_obj = form.save(commit=False)
  17. except Exception, e:
  18. flag = "Exception while processing. (%s)" % e
  19. else:
  20. flag = "Invalid form. (%s)" % form.errors
  21. if ipn_obj is None:
  22. ipn_obj = PayPalIPN()
  23. ipn_obj.initialize(request)
  24. if flag is not None:
  25. ipn_obj.set_flag(flag)
  26. else:
  27. # Secrets should only be used over SSL.
  28. if request.is_secure() and 'secret' in request.GET:
  29. ipn_obj.verify_secret(form, request.GET['secret'])
  30. else:
  31. try:
  32. ipn_obj.verify(item_check_callable)
  33. except Exception, e:
  34. flag = "Exception while processing. (%s)" % e
  35. ipn_obj.save()
  36. return HttpResponse("OKAY")

Just to elaborate a bit on the view, this line here ipn_obj.verify(item_check_callable) is the part that will invoke the sending of the signals. The verify() method can be found in paypal/models/standard/models.py which in turn calls the send_signals() method which can be found in paypal/models/standard/ipn/models.py

9. Final Stretch

So now we need to set up something to receive these signals. This can live anywhere in the project. The examples use models, so lets go with that.

  1. # models.py
  2. ...
  3. from paypal.standard.ipn.signals import payment_was_successful
  4. def show_me_the_money(sender, **kwargs):
  5. ipn_obj = sender
  6. # Undertake some action depending upon `ipn_obj`.
  7. if ipn_obj.custom == "Upgrade all users!":
  8. Users.objects.update(paid=True)
  9. print __file__,1, 'This works'
  10. payment_was_successful.connect(show_me_the_money)
  1. If everything works ok when we click the buy now button, console should print the line 'This works'
  2. So, go back to your domain/paypal page. Click on the buy button link, it should re-direct you to the Paypal sandbox page. Log in using the personal account you created in step 2. Mine is naiyun_1311850509_per@od-eon.com
  3. Go through the buying process and if everything works, you should see something like this in your console
  1. [05/Aug/2011 03:06:50] "GET /paypal/ HTTP/1.1" 200 1075
  2. /home/nai/GitProjects/cat/homepage/models.pyc 1 This works
  3. [05/Aug/2011 03:08:02] "POST /something/hard/to/guess/ HTTP/1.0" 200 4

Hope this tutorial has been helpful. If you have any questions, do leave them in the comments and I 'll do my best to answer them. 

UPDATE

Just in case anyone is running into DB related problems, django-paypal uses South to handle it's model creation. So running ./manage.py syncdb will *not* create the Paypal related tables.