django 1.2 stable çıktı

dün gece django 1.2 stable sürümü duyuruldu. django 1.2 ile birlikte benim gözüme çarpan önemli yenilikler;

multiple database desteği

tek proje içinde birden fazla veritabanı kullanmak artık “hacky” bir yönteme başvurmadan mümkün. settings.py içinde ayarlıyorsunuz o kadar.

model validation desteği

artık ilgili objenin bağlı bulunduğu modele göre otomatik validation yapabiliyoruz.

messages framework

(bu özellik için svn versiyonunu kullanıyordum) messages framework, kullanıcılara tek seferlik görünen hata/bilgi/başarı mesajları göstermenizi sağlayan bir çatı. daha önce django-flashmessages gibi eklentiler vardı bu iş için, ama çekirdeğe böyle bir özellik gelmesi harika oldu. messages.error(request, “hata mesajı”) formatında kullanıcı oturumuna ait hata ekleyebiliyorsunuz mesela.

template etiketlerinde akıllı kontroller

ifequal gibi zorlama bir tag yerine, -sonunda!- artık, template dosyalarında statement’lar kullanabilir durumda. şöyle ki;

{% ifequal request.user.username related_profile.username %}

yerine

{% if request.user.username == related_profile.username %}

kullanabiliyoruz. yaşayanlar bilir, çok can sıkıcıydı bu durum.

bunların dışında “template caching”, “email backend desteği” gibi yeni özellikler mevcut, tüm yeniliklerin listesi burada. 1.2′ye geçmeden önce, önceki sürüme göre deprecated olan değişikliklere göz atmayı unutmayın.

happy hacking! :)

kayıtlara geçmeliydi

grafiklere tıklayınca açılıyorlar.

xkcd arşivi, python, threading

Yıllardır XKCD arşivine tam olarak göz atamadım, bugün aşağıdaki diyaloğu -tekrardan- görünce artık vaktinin geldiğini anladım. [buraya random gülüş gelecek]

tüm arşivi indirmeye karar verince, yapmam gereken işlemin basit olduğunu gördüm, ama 700 küsür resmi tek thread/tek process çalışan bir python scriptiyle indirmek uzun sürecekti. bunun üstüne python’da thread pool mevzusunu hem öğrenmek hem de etinden suyundan faydalanmak için araştırdım ve çok kısa bir sürede hallettim. (akabinde python’a yine aşık oldum, tekrardan. literally.)

aşağıdaki betik threading kütüphanesi yardımıyla -biraz daha hızlıca- XKCD arşivini download ediyor. çalışması için sadece ilgili makinede python kurulu olması yeterli.

#!/usr/bin/env python
import Queue
import threading
import urllib
import re, os
 
"""
 XKCD dumper - http://www.xkcd.com
 emre yilmaz - www.darkbrown.org
"""
 
class Fetcher(threading.Thread):
    def __init__(self, queue):
        threading.Thread.__init__(self)
        self.queue = queue
        self.__control_download_path()
 
    def run(self):
        while True:
            comic_id = self.queue.get()
            try:
                url    = "http://www.xkcd.com/%s/" % comic_id
                source = urllib.urlopen(url).read()
                image  = self.retrieve_image_url(source)
                self.download_image(image, comic_id)
                print "downloading image: %s" % comic_id
            except Exception, error:
                print "image cannot be retrived for this id: %s" % comic_id
 
            # birinci belli, ikinci kim?
            self.queue.task_done()
 
    def retrieve_image_url(self, source):
        try:
            reply = re.findall("(http://imgs.xkcd.com/.*)</h3>", source)[0]
        except:
            print error
            reply = ""
 
        return reply
 
    def __get_extension(self, image_url):
        return re.findall("\.[a-z]{3,4}$", image_url)[0]
 
    def __control_download_path(self):
        if not os.path.isdir(XKCD.DOWNLOAD_DIR):
            os.mkdir(XKCD.DOWNLOAD_DIR)
 
    def download_image(self, comic_url, comic_id):
        extension     = self.__get_extension(comic_url)
        image_content = urllib.urlopen(comic_url).read()
        fp = open("%s/%s%s" % (XKCD.DOWNLOAD_DIR, comic_id, extension), 'wb')
        fp.write(image_content)
        fp.close()
 
queue = Queue.Queue()
 
class XKCD(object):
 
    THREAD_COUNT = 10
    COMIC_RANGE  = 732
    DOWNLOAD_DIR = 'images'   
 
    @staticmethod
    def fetch():
        # thread'leri olustur, ve siraya diz
        for i in range(XKCD.THREAD_COUNT):
            new_thread = Fetcher(queue)
            new_thread.setDaemon(1)
            new_thread.start()
 
        # siradaki islere veriyi daya
        for item in range(1, XKCD.COMIC_RANGE):
            queue.put(item)
 
        queue.join()
 
XKCD.fetch()

new project is on the way!

bir süredir işten arta kalan vakitlerde serdar ile birlikte geliştirdiğimiz proje iyice şekillenmeye başladı.

minik bir django projesi: 500t.org

kaynak kodlarını indirmek için buraya, ne olduğuna dair bilgi almak için buraya tıklıyoruz.

“güvenliksiz” güvenlik kodu

jquery için ajax-fancy-captcha diye bir eklenti yazılmış. ilgili eklentiden şu siteden haberdar oldum. eklentinin kendi sitesinde ise 122 adet yorum mevcut. google kayıtları coşmuş, ama ilgili eklentinin güvenliğiyle alakalı bir şey bulamadım. halbuki, ilginç derecede basit ve “aşılabilir” bir mantığı var kodun.

aşağıdaki kod da “aşılabilir” kısmını aşıyor :)

Read the rest of this entry »

django versus mysql: karakter seti problemleri

bildiğiniz üzere mysql varsayılan olarak “latin1_swedish_ci” karakter setiyle geliyor. django ise, her yerde – hemen hemen her yerde değil, her yerde- unicode kullanıyor.

modellerinizi yazdıktan sonra “python manage.py syncdb” yapıp, tablolar yaratıldıktan sonra “süper kullanıcı” girmek isterseniz, standart bir mySQL kurulumunda (ubuntu gnu/linux) aşağıdaki hatayı alıyorsunuz:

...
Warning: Incorrect string value: '\xC4\xB1nc\xC4\xB1...' for column 'name' at row 1

django, her şeyi her yerde unicode kullanmasına rağmen, mysql’de tabloları yaratırken karakter seti olarak utf8_*_ci belirtmiyor. hatta belirtmek de istemiyor buradaki hata girdisinde görebileceğiniz üzere. O kısım, mysql sunucunun standart ayarlarıyla çalışıyor. (Django’nun “it is not a bug, it is a feature!” anlayışını sorgulamaya başladım son zamanlarda.)

her neyse, sorunun çözümü basit. mysql sunucusunu standart olarak utf8 ile çalışacak şekilde ayarlamanız gerekiyor. ubuntu için (ve bir çok linux dağıtımı için) root yetkileriyle aşağıdaki yolu takip edebilirsiniz:

/etc/mysql/conf.d/ dizini içine charset.cnf şeklinde bir dosya açın ve içine aşağıdakileri girin ve kaydedin.

[mysqld]
default-character-set=utf8
default-collation=utf8_general_ci
character-set-server=utf8
collation-server=utf8_general_ci
init_connect=’SET collation_connection = utf8_general_ci’
init_connect=’SET NAMES utf8′
skip-character-set-client-handshake

uyarı: uzattığınız dosyanın uzantısı kesinlikle .cnf olmaılıdır. mysql, sadece .cnf uzantılı dosyaları dikkate alıyor.

mysql sunucusunu yeniden başlatın:

/etc/init.d/mysql restart

sorun kalmayacaktır ;)

django için “gerçek” @login_required dekoratörü

django-developers ve django-users mail listelerini takip ediyorum. Django ile uygulama geliştirmeye meraklı arkadaşlara da tavsiye ederim. geçenlerde gördüğüm şu thread ise dikkat çekici.

django auth bünyesinde ufak bir decorator barındırıyor. view dosyalarınızın başına koyduğunuz @login_required ile o kısma sadece sisteme giriş yapmış kullanıcıların girebilmesini basitçe sağlıyorsunuz. Django’nun auth modülünün tasarımında kullanıcı pasif durumda olsa bile, authenticated (yetkilendirilmiş) sayılıyor. ilgili dekoratörümüz login_required ise gerekli kontrolü yaparken is_authenticated() değerine bakıyor.

sorun şu ki, pasif bir kullanıcı giriş yapmaya çalıştığında hesap bilgilerini doğru girerse is_authenticated() True dönüyor. is_active kolonu False dönse de. bu da görmemelere gereken sayfaları görebiliyor, yapamamaları gereken işlemleri yapabiliyor olmaları demek.

işin kısası, pasif kullanıcılarınız pek de pasif değiller eğer login_required dekoratörüne güvendiyseniz. aşağıda kendi projem için yazmış olduğum decorator var, projeniz içinde herhangi bir yere gömüp view fonksiyonlarınızdan önce @activeMemberOnly şeklinde kullanabilirsiniz.

from django.contrib.auth import REDIRECT_FIELD_NAME
from django.contrib.auth.decorators import user_passes_test
 
def activeMemberOnly(function=None, redirect_field_name=REDIRECT_FIELD_NAME):
    actual_decorator = user_passes_test(
        lambda u: u.is_authenticated() and u.is_active,
        redirect_field_name=redirect_field_name
    )
    if function:
        return actual_decorator(function)
    return actual_decorator

ubuntu 9.10 için sphinx kurulumu

yeni projemiz için sphinx full text arama motorunu kullanmayı planlıyorum. Solr ile geçirdiğimiz güzel günler söz konusu olsa da, arama işi için ayrı bir server ayakta tutmak ve bununla HTTP üzerinden iletişime geçmek, türkçe karakter problemleriyle uğraşmak yerine sphinx’i incelemeye aldım. (memnun kalmazsam sırada xapian var.)

ilginçtir ki sphinx’in ubuntu ya da başka bir dağıtım için paketi yok. kendiniz derlemeniz gerekiyor. güncel bir ubuntu’da aşağıdaki adımlarla sphinx’i başarılı bir şekilde kurabilirsiniz.

ilk önce mysql-devel paketlerini kurmak gerekiyor. (bunlar yüzünden biraz zaman kaybettim kurulumda..)

sudo apt-get install libmysqld-dev libmysqlclient-dev

bu aşamadan sonra sphinx’i indirip, arşivi açtıktan sonra sırasıyla aşağıdaki komutları verin:

./configure --with-prefix=/usr/local/sphinx
make
sudo make install

işlemler bittikten sonra konsolda search yazarsanız şöyle bir ekran sizi karşılayacaktır:

root@yunusemre-desktop:/home/yunusemre/Desktop/sphinx-0.9.9# search
Sphinx 0.9.9-release (r2117)

her şey sorunsuz bittiyse gönül rahatlığıyla sphinx dökümantasyonuna atlayabilirsiniz :)

writing view decorators in django

what the heck is decorators

decorators are the funniest part of python. decorators allow you to execute your additional code at the entry and exit points of a function or a class with a clear syntax. it is very handy when you need to do locking, logging, tracking, caching etc. with your function.

decorators on air

that is the simplest usage of decorators in python.

def sayWelcome(originalFunction):
 
    def decorated():
         print("welcome, my bloody sexy function")
         originalFunction()
    return decorated
 
@sayWelcome
def myfunction():
    print("hi, i am the one who will be decorated")
 
myfunction()

it’s output will be:

emre@amy:~$ python test.py
welcome, my bloody sexy function
hi, i am the one who will be decorated
emre@amy:~$

you can check out some details in here.

applying it to your django project

if you ever used django auth, you must be aware of bundled decorators in django like @authentication_required, @permission_required.

let’s write a “@limited_access” decorator for django auth. If you don’t wanna deal with user groups/permissions this is for you. This allows you to point views for the users you selected.

from django.http import HttpResponseRedirect
class limited_access(object):
    def __init__(self, user_list):
        self.user_list = user_list
    def __call__(self, view_function):
        def wrapped_function(request, *args, **kwargs):
            if not request.user.username in self.user_list:
                # user is not in access_list, so redirect him/her
                return HttpResponseRedirect("http://noaccess.yoursite.com") 
            else:
                return view_function(request, *args, **kwargs)
        return wrapped_function

use this in views like this;

@limited_access(["emre", "mary",])
def test_view(request):
    return HttpResponse("this view is just for emre and mary.")

any suggestions and comments are wellcome.