Tag Archives: Python

Genel

Django Modellerinde ManyToMany Alanların Belirli Bir Filtreye Göre Limitlenmesi

Sistem genelinde taglayabildiğiniz tüm nesne tipleri ile ilgili tag verilerinin tek bir tabloda tutulduğunu düşünelim. Bu durumda tag kayıtlarını bir şekilde sınıflandırmamız ve ilgili nesnelerin sadece kendisiyle ilgili tipteki taglere sahip olabilmesini sağlamalıyız. Bu yapıyı sağlayabilmek için ManyToManyField tanımlaması yapılırken limit_choice_to parametresi kullanılarak ilişkili tablonun neye göre filtreleneceğini dict olarak tanımlamamız gerekir.

Böylece django admin tarafında da kullanıcının ilgili kaydı editlerken manytomany alanda yalnızca kendisiyle ilgili kayıtları görmesi/seçebilmesi sağlanır.

Aşağıda örnek veri modeli yeralıyor.

from django.db import models


class Tag(models.Model):
    TAG_TYPES = (
        ('article', 'Article'),
        ('video', 'Video'),
    )
    slug = models.SlugField(max_length=50)                                     
    name = models.CharField(max_length=50)   
    tag_type = models.CharField(max_length=30)    


class Article(models.Model):
    slug = models.SlugField(max_length=80)
    title = models.CharField(max_length=80)
    content = models.TextField()
    tags = models.ManyToManyField(Tag, limit_choices_to={'tag_type': 'article'})


class Video(models.Model):
    slug = models.SlugField(max_length=50)
    name = models.CharField(max_length=50)
    url = models.UrlField()
    tags = models.ManyToManyField(Tag, limit_choices_to={'tag_type': 'video'})
Genel

__getattr__ vs. __getattribute__

__getattr__ Kullanımı:
__getattr__, uygulamada bir sınıfın varolmayan bir özelliğine ulaşılmak istendiğinde çağırılen bir magic metoddur.

class Order(object):
    def __init__(self, order_id=None, amount=None, currency=None, *args, **kwargs):
        self.order_id = None
        self.amount = amount
        self.currency = currency

    def __getattr__(self, attr):
        print "%s is called" % attr


order = Order(order_id='00001', amount=100, currency='TRL')
print order.amount
print order.original_amount


Çıktı:

100
original_amount is called
None

__getattribute__ Kullanımı:
__getattribute__, uygulamada bir sınıfın herhangibir özelliğine ulaşılmak istendiğinde çağırılen bir magic metoddur.

class Order(object):
    def __init__(self, order_id=None, amount=None, currency=None, *args, **kwargs):
        self.order_id = None
        self.amount = amount
        self.currency = currency

    def __getattribute__(self, attr):
        print "%s is called" % attr


order = Order(order_id='00001', amount=100, currency='TRL')
print order.amount
print order.original_amount

Çıktı:

amount is called
None
original_amount is called
None
Genel

Python ile Web Crawling

Aslında üstüne pek de yazı yazılası bir konu değil ama yine de belki meraklısının işine yarar :)

Herhangibir dille web sitesi crawle etmek için bilinmesi gereken 3 şey

1- Nasıl http isteği gönderebilirim ?
2- Nasıl xpath sorgusu gerçekleştirebilirim ?
3- Elde ettiğim elemanların özelliklerine nasıl ulaşabilirim ?

Dilerseniz konuyu bu başlıklar altında inceleyelim.

1- Nasıl http isteği gönderebilirim ?

Python ile http request gönderebilmek için requests veya urllib2 kütüphanelerini kullanabilirsiniz.

Ben bu yazıdaki örneklerde urllib2 kullanacağım.

Örnek GET isteği:

	url = 'http://edition.cnn.com/'
	try:
		request = urllib2.Request(url)
		response = urllib2.urlopen(request).read()
	except urllib2.URLError:
		print "Connection failed. Url: %s" % url

2- Nasıl xpath sorgusu gerçekleştirebilirim ?

Python ile xpath sorgusu gerçekleştirmek için xml.ElementTree veya lxml kullanabilirsiniz.

Ben bu yazıdaki örneklerde lxml kullanacağım.

Aşağıdaki örnekde, CNN.com sitesindeki haber kategorilerini sorguluyoruz.
Örnek Xpath sorgusu:

	from lxml import html
	query = '//*[@id="intl-menu"]/li/a'
	root = html.fromstring(response)
	categories = root.xpath(query)

3- Elde ettiğim elemanların özelliklerine nasıl ulaşabilirim ?

Bir önceki örnde kullandığım lxml kütüphanesini kullanıyorsanız, xpath sorgusu sonuncunda dönen elemanın DOM özelliklerine ulaşmak için get() metodunu, içindeki metne ulaşmak için text özelliğini, alt elemanlarına ulaşmak için getchildren() metodunu kullanabilirsiniz.

for category in categories:
	children = category.getchildren()
	text = children and children[0].text or category.text
	print text, category.get('href')

Son olarak herşeyi bir araya getirelim.

from lxml import html
import urllib2

url = 'http://edition.cnn.com/'
try:
	request = urllib2.Request(url)
	response = urllib2.urlopen(request).read()
except urllib2.URLError:
	print "Connection failed. Url: %s" % url

query = '//*[@id="intl-menu"]/li/a'
root = html.fromstring(response)
categories = root.xpath(query)

for category in categories:
	children = category.getchildren()
	text = children and children[0].text or category.text
	print text, category.get('href')

Elde edeceğimiz sonuç aşağıdaki biçimde olacaktır.

Home /
Video /video/
World /WORLD/
U.S. /US/
Africa /AFRICA/
Asia /ASIA/
Europe /EUROPE/
Latin America /LATINAMERICA/
Middle East /MIDDLEEAST
Money http://money.cnn.com/INTERNATIONAL/
World Sport /SPORT/
Entertainment /SHOWBIZ/
Tech /TECH/
Travel http://travel.cnn.com/
iReport http://ireport.cnn.com/
Genel

Django REST Framework İle RESTFul Web Servisi Geliştirme

django-restframework-logo
Django REST Framework, Django uygulamalarınız için RESTFul web servisi geliştirmenize olanak sağlayan oldukca kullanışlı bir python paketi. Benzeri diğer bir python kütüphanesi olan Tastypie ile karşılaştırdığımda ilk bakışta anlaması/öğrenmesi oldukca kolay göründüğünü söylemeliyim.

Django REST Framework ile RESTFul web servisi geliştirmek, aslında bildiğimiz django view lerini kullanarak uygulama geliştirmeye oldukca benziyor. Kendi view sınıf ve dekoratörleri djangodaki class ve function based viewler gibi kodlama yapmanıza olanak sağlıyor.

Hızlı başlangıç için: şuradaki
Kütüphanenin güncel kaynak kodları için: buradaki
bağlantıları inceleyebilirsiniz.

Dilerseniz hemen ilk örnek uygulamamızı yazmak için yeni bir virtual environment oluşturalım ve aktif hale getirelim.

$ virtualenv myblog-env
$ cd myblog-env
$ source bin/activate

Ben bu örnek için Django 1.6.6 kullanacağım.

$ pip install django==1.6.6

ve… Django REST Framework paketinin kurulumunu gerçekleştirelim. Ben bu örnek için paketin bugünki güncel sürümü olan 2.4.0 sürümünü kuracağım.

$ pip install djangorestframework==2.4.0

Evet… Artık geliştirmeye hazırız. Hemen django projemizi ve uygulamamızı oluşturalım.

$ django-admin.py startproject myblog
$ cd myblog
$ python manage.py startapp blog

Hemen basitce bir Post modeli oluşturalım.

myblog/blog/models.py:

from django.contrib.auth.models import User
from django.db import models


class Post(models.Model):
	title = models.CharField(length=80)
	content = models.TextField()
	author = models.ForeignKey(User)

Uygulamamızı settings.py dosyasındaki INSTALLED_APPS bölümüne ekleyelim.

myblog/myblog/settings.py:

...
INSTALLED_APPS = (
...
	'rest_framework',
	'blog',
...
)
...

Tablolarımızı yaratalım…

$ python manage.py syncdb

An itibarıyla temel bir uygulama geliştirebilecek şartlara sahibiz. Bir adet veri modelimiz var ve bir API vasıtasıyla bu modele veri girişi yaptırmak istiyoruz. Başlarken, Django REST Framework kütüphanesi ile web servisi geliştirmenin, class veya function based viewler ile bildiğimiz django uygulaması geliştirmekten çok da farklı olmadığından söz etmiştik. Bilinen django view leri, sunu katmanından gelen veriyi işleyip HTTPResponse tipinde yeni bir obje dönmekteydi. Django REST Framework tarafında ise kullanıcıdan alınan veriler veya kullanıcı tarafına gönderilen veriler Serializer objeleri üzerinden API viewleri ile haberleşir.

Serializer nesneleri, uygulama altında serializers.py isimli python dosyalarında barındırılırlar.

Post modülümüz için yeni bir Serializer objesi oluşturalım.

myblog/blog/serializers.py:

from blog.models import Post
from django.contrib.auth.models import User
from rest_framework import serializers


class UserSerializer(serializer.HyperlinkModelSerializer):
	class Meta:
		model = User
		fields = ('url', 'username', 'email', 'groups')


class PostSerializer(serializer.HyperlinkModelSerializer):
	class Meta:
		model = Post
		fields = ('title', 'content', 'author')

Serializer objeleri doğrudan modeli temsil edebilecekleri gibi serializer.Serializer sınıfından türetilerek özel bir veri tipi için de kişiselleştirilmiş Serializer objeleri oluşturulabilir.

Sıra geldi kullanıcıdan gelen istekleri karşılamaya. Model ve serializer kodlamasını tamamladığımıza göre artık Post modelimiz için yeni bir view(lar) oluşturabiliriz. Django REST Framework başta da belirttiğimiz gibi aynı Django uygulamalarında olduğu gibi class ve function based viewlar oluşturabilmemize izin veriyordu. Ben örnek uygulamamız için kolay okunabilmesi nedeniyle class base view tercih edeceğim.

from blog.serializers import PostSerializer

from django.http import Http404

from rest_framework import views
from rest_framework.response import Response
from rest_framework import status


class PostList(views.APIView):
	def get(self, request, format=None):
		posts = Post.objects.all()
		serializer = PostSerializer(posts, many=True)
		return Response(serializer.data)

	def post(self, request, format=None):
		serializer = PostSerializer(data=request.DATA)
		if serializer.is_valid():
			serializer.save()
			return Response(serializer.data, status=status.HTTP_201_CREATED)
		return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

class PostDetail(views.APIView):
    def get_object(self, pk):
        try:
            return Post.objects.get(pk=pk)
        except Post.DoesNotExist:
            raise Http404

    def get(self, request, pk, format=None):
        post = self.get_object(pk)
        serializer = PostSerializer(snippet)
        return Response(serializer.data)

    def put(self, request, pk, format=None):
        post = self.get_object(pk)
        serializer = PostSerializer(post, data=request.DATA)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk, format=None):
        post = self.get_object(pk)
        post.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

Gördüğünüz üzere Post modeli için GET, PUT, POST, DELETE isteklerine cevap verebilen detail ve list viewlerimizi oluşturduk.

Django REST Framework class based view lerin method handle etmeyen halleri gibi tanımlayabileceğimiz ViewSet denilen uyarlamaları da kullanabilmemize olanak sunar. ViewSetler farklı kütüphanelerde Resource gibi farklı şekillerde de isimlendirilerler.

from blog.models import Post
from blog.serializers import PostSerializer

from rest_framework import viewsets


class PostViewSet(viewsets.ModelViewSet):
	queryset = Post.objects.all()
	serializer_class = PostSerializer

Bu yazı Django REST Framework kütüphanesini başlangıç seviyesinde tanıtmayı amaçladığından şimdlilik bu bölümü anlatmayacağım. Merak edenler dökümaın şu bölümünü inceleyebilirler.

Class based viewlerimizi oluşturduğumuza göre sıra geldi router a viewlerimizi kayıt etmeye.

myblog/myblog/urls.py:

from django.conf.urls import patterns, url
from rest_framework.urlpatterns import format_suffix_patterns
from blog import views

urlpatterns = patterns('',
    url(r'^post/$', views.PostList.as_view()),
    url(r'^post/(?P<pk>[0-9]+)/$', views.PostDetail.as_view()),
)

urlpatterns = format_suffix_patterns(urlpatterns)

Artık uygulamamız istekleri kabul etmeye hazır. Örnek istek oluşturup yanıtını göstermeye üşendiğim için işin en zevkli kısmınıı size bırakıyorum :)

Django REST Framework konusunda anlatılacak çok şey var. Kullanışlı ve son derece esnek. Detaylı dökümantasyon ve örnekler için Django REST Framework Resmi Web Sitesi‘ni ziyaret edebilirsiniz.

Genel

Django Framework Üzerinde Sinyal Kullanımı

Django, sinyaller vasıtasıyla uygulama genelinde meydana gelen herhangibir olayı, sinyali dinleyen alıcılara bildirir. Geliştiriciler, djanog ile birlikte gelen hali hazırdaki sinyalleri kullanabildikleri gibi (Bkz. Django ile birlikte gelen hali hazırda sinyaller) kendileri de geliştirdikleri uygulamaya özel sinyaller tanımlayabilirler.

Django uygulamalarında sinyaller, alıcılar vasıtasıyla dinlenir. Django dünyasında alıcılar, bilindik python fonksiyonlarıdır.

Farzı misal bakınız bir sinyal alıcısı:

from django.core.signals import request_finished
from django.dispatch import receiver

@receiver(request_finished)
def my_callback(sender, **kwargs):
    print("Request finished!")

Alıcıları, @receiver dekoratörü ile sinyallere bağlayabildiğiniz gibi

request_finished.connect(my_callback)

şeklinde de bağlayabilirsiniz.

Kullanıcı Tanımlı Sinyaller:

Django, bizlere http isteklerinin durumu ve veritabanı işlemleri ile ilgili dahili sinyaller sağlıyor. Ancak bazı durumlarda geliştirdiğiniz uygulamaya spesifik bazı özel sinyaller tanımlamanız gerekebilmektedir. Böyle durumlarda sizler de Django nun django.dispath modülünde yeralan Signal sınıfını kullanabilirsiniz.

Sinyal tanımlamalarını ilgili uygulamanın altında signals.py dosyası içinde gerçekleştirilir.

Aşağıdaki örnekte, e-ticaret uygulamamızın sipariş tamamlandığında order_completed sinyalini tetiklemesini sağlıyoruz.

öncelikle order_completed sinyalini tanımlayalım:

# order/signals.py

import django.dispath

order_completed = django.dispath.Signal(providing_args=['order'])

Şimdi de sipariş tamamlandığında bu olayı tetikleyelim.

# order/services.py

from order.signals import order_completed

class OrderManager(object):
    def create_order(self, *args, **kwargs):
        # siparisle ilgili islemler...
        #
        # yeni siparis yaratiliyor.
        order = Order.objects.create(order_nr='00001234')

        # ...ve siparis tamamlandi olayini tetikliyoruz.
        order_completed.send(sender=self, order=order)
        return order

Herhangibir sipariş geldiğinde, sipariş order tablosuna yazıldıktan hemen sonra order_completed sinyalinin gönderildiğini görebiliyoruz. Artık diledğimiz bir veya daha fazla alıcı ile bu sinyali dinleyerek bu sinyalle ilgili herhangibir işlemi gerçekleştirebiliriz. Yeri gelmişken Django uygulamalarında sinyallerin senkron çalıştığını söylemekte yarar var. Alıcı tarafında meydana gelen herhangibir darboğaz veya hata, uygulamanın yavaşlamasına veya kırılmasına neden olabilir.

Şimdi de aynı örnek için order_completed olayını dinleyen bir alıcı kodlayalım. Alıcılar konusunda malesef standart bir yaklaşım yok. İnternette görebileceğiniz örneklerde alıcılar şayet modelle ilgili bir alıcıysa ilgili olduğu uygulamının models.py dosyasına sinyal konusu olan model sınıfının hemen altına yazılıyor.

Şahsi görüşüm, sinyal alıcılarının business logic içermesi ve modelle ilgisinin sadece kendisiyle olan bağ olması nedeniyle ayrı bir yerde tutulması gerektiği yönünde. Bu nedenle ben, yeraldığım projelerde sinyal alıcılarını ilgili uygulamanın altında receivers.py içinde barındırmayı tercih ediyorum. Tabi bu durumda alıcıların uygulama genelinde işlevlerini yerine getirebilmeleri için merkezi biryerlerde import edilmeleri gerektiğinden import işlemini urls.py içerisinde gerçekleştiriyorum.

Aşağıdaki örnek alıcı, sipariş tamamlandığında yöneticiye sipariş tamamlandı maili gönderir.

# order/receivers.py

from django.dispatch import receiver
from django.core.mail import mail_admins
from order.signals import order_completed

@receiver(order_completed, 
          dispatch_uid="order.receivers.send_order_notification")
def send_order_notification(sender, order, **kwargs):
    mail_admins(subject='order completed #%s' % order.order_nr,
                message='Received a new order from site. '
                'New order number is: #%s' % order.order_nr)

Alıcının işlevini yerine getirebilmesi için urls.py dosyasında recivers.py ın içerdiği alıcıları import ediyoruz.

# urls.py

from order.receivers import *

# ...

Detaylı bilgi için Django Framework dökümantasyonundaki ilgili bölümü inceleyebilirsiniz.

Genel

Django Uygulamalarının Apache Üzerinde Sanal Ortam (virtualenv) İle Birlikte Çalıştırılması

Django framework ile geliştirdiğiniz python uygulamalarınızı, apache2 üzerinde virtualenv ile birlikte çalıştırmak için aşağıdaki işlemleri sırasıyla gerçekleştirmelisiniz.

– Şayet sunucunuzda mevcut bir apache kurulumu varsa aşağıdaki apache bileşenlerinin kurulumunu gerçekleştiriniz.

$ sudo apt-get install apache2.2-common apache2-mpm-prefork apache2-utils libexpat1 ssl-cert

– Apache yi restart ediniz ve tarayıcınızdan istek göndererek halen sorunsuz ve çalışır durumda olduğundan emin olunuz.

$ sudo /etc/init.d/apache2 restart

– Apache için wsgi modülünün kurulumunu gerçekleştiriniz.

$ sudo apt-get install libapache2-mod-wsgi

– Virtual environment üstündeki python projeniz için /etc/apache2/sites-available/ dizininde aşağıdaki gibi yeni bir sanal host dosyası oluşturunuz. (Örnek dosyamızın adi sizinsite.com olsun)

<VirtualHost *:80>
    ServerName sizinsite.com
    ServerAlias www.sizinsite.com
    WSGIScriptAlias / /var/www/sizinsitecom-env/project/sizinsitecom-src/wsgi.py
    WSGIProcessGroup sizinsitecom
    WSGIDaemonProcess sizinsitecom processes=5 python-path=/var/www/sizinsitecom-env/project/sizinsitecom-src:/var/www/sizinsitecom-env/lib/python2.7/site-packages threads=1
    Alias /static/ /var/www/sizinsitecom-env/project/sizinsitecom-src/static/
    <Directory /var/www/sizinsitecom-env/project/sizinsitecom-src/>
        <Files wsgi.py>
            Order deny,allow
            allow from all
        </Files>
    </Directory>
    <Directory /var/www/sizinsitecom-env/project/sizinsitecom-src/static/>
        Options Indexes FollowSymLinks MultiViews
        AllowOverride All
        Order allow,deny
        allow from all
    </Directory>


    ErrorLog ${APACHE_LOG_DIR}/sizinsitecom-error.log

    # Possible values include: debug, info, notice, warn, error, crit,
    # alert, emerg.
    LogLevel warn

    CustomLog ${APACHE_LOG_DIR}/sizinsite-access.log combined
</VirtualHost>

– sanal host u aktif hale getirin.

$ sudo a2ensite sizinsite.com

– Yeni sanal host un aktif olması için apache servisini yeniden başlatın veya konfigürasyonu reload edin.

$ sudo service apache2 restart

Hepsi bu kadar.

Programa Dilleri ve Çatıları

Django Framework Template Etiketlerinde User Objesine Erişmek

Aslında django kullanıcıları için muhtemelen çok basit bir konu ancak benim gibi yeni başlayanlar için sorun olabilecek konulardan biri olduğu için yazıyı sizinle paylaşmak istedim.

Problem:
Django öğrenmek için yazdığım blog projesinde kullanıcıya gösterdiğim blog postunun yeni comment girişi alanını, comment blog postuna veya başka bir comment e yanıt olarak girilebileceğinden inclusion_tag yaptım. Ancak inclusion tag in include ettiği template içinde user objesine ulaşamıyorum.

Çözüm:
Uzun stackoverflow okumlarından sonra inclusion_tag e parametre olarak takes_context göndersem de tag e parametre olarak gelen context içinde user objesinin gelmediğini öğrendim. Bunun yerine view tarafında, ilgili actionda return edilen render fonksiyonuna parametre olarak, context_instance için RequestContext göndermem gerekiyormuş. Böyle olunca tag e parametre olarak gelen context içinde user objesinin yeraldığını gördüm.

Problemi çözmek için aşağıdaki adımları uyguladım:

1. view tarafında django.template içindeki RequestContext objesini import ettim.
myapp/views.py:

from django.template import RequestContext

2. İlgili template tag i kullanacağım view ın aksiyonunda return ettiğim render() fonksiyonuna context_instance olarak RequestContext i gönderdim.
myapp/views.py:

def viewpost(request, post_id):
    post = get_object_or_404(Post.objects.select_related('author'), pk=post_id)
    ...
    return render(
        request, 'blog/post/detail.html', 
        {'post' : post}, 
        context_instance=RequestContext(request)
    )

3. template tag tarafında include edeceğim template e veri olarak user objesini gönderdim.
myapp/templatetags/myapp_extra.py:

@register.inclusion_tag('blog/post/blocks/comment/new.html', takes_context=True)
def show_comment_input(context):
    return {'user' : context['user']}

4. Ve… template tag imin include ettiği template kodu.
blog/post/blocks/comment/new.html:

<form action="#" method="POST" class="well" style="margin:0px">
    <h3>Write your comment or reply</h3>

    {% if not user.is_authenticated %}
    <div class="controls controls-row">
        <input type="text" placeholder="Your Name" class="span4" />
        <input type="text" placeholder="E-Mail Address" class="span4" />
    </div>
    {% endif %}
    <div class="controls controls-row">
        <textarea class="span8" placeholder="Your Comments"></textarea>
    </div>
    <div class="controls controls-row">
        <input type="submit" class="btn btn-primary" value="Submit" />
    </div>
    {% csrf_token %}
</form>

Elimdeki proje bittikten sonra gelen Edit:
Context instance olarak zaten request objesi gönderilmiş ve user objesi context’in içinde halihazırda mevcut. Dolayısıyla inclusion tag tanımlarken aşağıdaki gibi bir tanımlama yapmamıza gerek yok. Zaten takes_context dediğimizde context içeriği include edilen template e push ediliyor.
Aşağıdaki değişikliği yapabilirsiniz:
Eski:

@register.inclusion_tag('blog/post/blocks/comment/new.html', takes_context=True)
def show_comment_input(context):
    return {'user' : context['user']}

Yeni:

@register.inclusion_tag('blog/post/blocks/comment/new.html', takes_context=True)
def show_comment_input(context):
    pass

Kaynaklar:
http://stackoverflow.com/questions/2160261/access-request-in-django-custom-template-tags
http://stackoverflow.com/questions/3337419/django-user-is-authenticated-works-some-places-not-others