Monthly Archives: Mayıs 2013

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

Programa Dilleri ve Çatıları

Zend_Auth ile Kimlik Doğrulama İşleminin Gerçekleştirilmesi

Zend_Auth, Zend Framework’un oldukça kullanışlı kütüphanelerinden birisidir. Çeşitli kimlik doğrulama servislerine sağladığı destekle projenizin kimlik doğrulama mekanizmasını hızlıca biryerden biryere taşıyabilmenize olanak sağlar. Örneğin Zend üzerinde geliştirdiğiniz intranetinizin kimlik doğrulama işlemini Zend_Auth_Adapter_DbTable kullanarak veritabanındaki kullanıcı kayıtları üzerinden gerçekleştirirken, küçük bir geliştirme ile kimlik doğrulama operasyonunuzu şirketinizdeki LDAP sunucusu üzerine taşıyarak, uygulamanızı firmanızdaki e-posta hesapları, domain kullanıcıları ve IP telefonlar gibi sistemlerle aynı authentication servisini kullanarak kolayca yönetmeniz mümkündür.

Zend_Auth, bize aşağıdaki adaptörleri kullanarak kimlik doğrulama işlemi yapabilmemize olanak sağlar:

  • Zend_Auth_Adapter_DbTable
  • Zend_Auth_Adapter_Digest
  • Zend_Auth_Adapter_Http
  • Zend_Auth_Adapter_Ldap
  • Zend_Auth_Adapter_OpenId

Yukarıdaki adaptörleri kullanabildiğiniz gibi sizler de Zend_Auth_Adapter_Interface arayüzüne tabi yeni bir adaptör yazarak kendi kimlik doğrulama servisinizi geliştirebilirsiniz.

Bugün, konuyu temel düzeyde anlatabilmek adına sizlerle veritabanı adaptörünü kullanarak kimlik doğrulama işleminin nasıl gerçekleştirildiğini inceleyeceğiz.

Kimlik Doğrulama İşleminin Zend_Auth_Adapter_DbTable kullanılarak Gerçekleştirilmesi:

Zend_Auth_Adapter_DbTable, kimlik doğrulama işleminin veritabanındaki bir tabloda yeralan kullanıcı kayıtlarını kullanarak gerçekleştirilmesini sağlar. İnşası sırasında bizden aşağıdaki konfigürasyon verilerini beklemektedir:

zendDb: Zend_Db_Adapter_Abstract tipindeki veritabanı bağlantısını içerir.

tableName: Bu parametre, kullanıcı kimlik verilerinin bulunduğu veritabanındaki tablonun adını içermelidir.

identityColumn: Bu alan kullanıcı adı veya e-posta adresi gibi uniq bir kimlik verisi içermelidir.

credentialColumn: Kullanıcı adı/parola şemasındaki kimlik doğrulama yapılarında bu alan, kullanıcı parolasını içermelidir.

credentialTreatment: Pek çok durumda kullanıcı parolasının farklı algoritmalarla şifrelenmesi istenir. Bu alanda şifrelemenin nasıl gerçekleştirileceği metin olarak belirtilir. (MD5(?) gibi)

Ben inceleyeceğimiz örneklerde MySQL tercih ettim. Sizler dilerseiniz, MonboDB, SQLLite veya başka bir veritabanı kullanabilirsiniz.

İlk olarak kullanıcı tablomuzu oluşturalım.

CREATE TABLE `intranet_users` (
`id` INTEGER  NOT NULL AUTO_INCREMENT, 
`username` VARCHAR(50) UNIQUE NOT NULL, 
`password` VARCHAR(32) NULL, 
`real_name` VARCHAR(150) NULL,
PRIMARY KEY (id));

Veritabanı bağlantımızı oluşturalım.

$db = Zend_Db::factory('Pdo_Mysql', array(
    'host'     => '127.0.0.1',
    'username' => 'webuser',
    'password' => 'xxxxxxxx',
    'dbname'   => 'myintranetapp'
));

Yeni bir Zend_Auth_Adapter_DbTable örneği oluşturarak konfigürasyon verilerini örneğin inşası sırasında parametre olarak gönderiyoruz.

$authAdapter = new Zend_Auth_Adapter_DbTable(
    $db,
    'intranet_users',
    'username',
    'password'
);

Kullanıcı adı ve parola bilgilerimizi adaptöre tanımlıyoruz.

authAdapter->setIdentity('myusername')
           ->setCredential('mypassword');

Oturum açıyoruz.

$auth = = Zend_Auth::getInstance();
$result = $auth->authenticate($authAdapter);

Kimlik doğrulama işleminin başarılı/başarısız olma durumunu kontrol ediyoruz.

if( $result->isValid() ) {
    //Oturum basariliyse birseyler yap.
} else {
    //Oturum basarisizsa birseyler yap.
}

Kullanıcımızın kimliğini doğruladık. Ancak bu yeterli değil. Kimlik doğrulamasının başarılı olması durumunda oturum bilgisinin isteğe bağlı olarak dosya sistemi, memory veya veritabanı gibi biryerde saklanması gerekmektedir. Zend_Auth objesi bu bilgileri kayıt etmek için varsayılan olarak PHP Session objesini kullanır. Sizler uygulamalarınızda oturum verilerini dilerseniz veritabanı, dosya sistemi veya Zend_Auth_Storage_Interface arayüzüne uygun kendi geliştireceğiniz farklı bir depolama alanında saklayabilirsiniz.

if( $result->isValid() ) {
    $storage = $auth->getStorage();
    $storage->write($authAdapter->getResultRowObject(array('username', 'real_name')));
} else {
    //Oturum basarisizsa birseyler yap.
}

Projenizin herhangibir yerinde oturum durumunu kontrol etmek isterseniz:

$auth = Zend_Auth::getInstance();
if($auth->hasIdentity()) {
  echo $auth->getIdentity();
}

Oturumu sonlandırmak isterseniz:

$auth = Zend_Auth::getInstance();
$auth->clearIdentity();

Zend_Auth kütüphanesini temel düzeyde inceledik. Sizler de bu kütüphaneyi kolayca uygulamalarınıza uyarlayabilirsiniz. Zend_Auth ile ilgili daha fazla bilgi edinmek için http://framework.zend.com/manual/1.12/en/zend.auth.html sayfasını ziyaret edebilirsiniz. Bu blog yazısı ile Zend_Auth kütüphanesini kullanabileceğinizi düşünüyorsanız, diğer kimlik doğrulama adaptörlerinin kullanımı, yeni bir kimlik doğrulama adaptörünün geliştirilmesi ve diğer session storage adaptörlerinin kullanımı konularını incelemenizi öneririm.

Programa Dilleri ve Çatıları

YII Üzerinde ActiveRecord Kullanımı

Ben yazdığım uygulamalarda uzun uzun SQL yazmayı sevmeyenlerdenim. Neden mi ? Pis duruyor arkadaş! Güvenli olsun diye göndereceğim her bir parametreyi teker teker escape etmek zor geliyor. Projelerimde basitliği nedeniyle zaman zaman ActiveRecord u tercih ediyorum. Bugün sizlere YII üzerinde ActiveRecord kütüphanesinin nasıl kullanılacağı konusundan kısaca bahsedeceğim.

Active Record Yenilir mi İçilir mi ?

ActiveRecord, bildiğiniz DbTable türünden bir PHP kütüphanesidir. Veritabanındaki herbir tablo, CActiveRecord sınıfından türeyen bir sınıf ile temsil edilir.

Şöyle bir tablomuz olsa:

CREATE TABLE tbl_post (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    title VARCHAR(128) NOT NULL,
    content TEXT NOT NULL,
    create_time INTEGER NOT NULL
);

Oturup PDO ile bu tabloya kayıt insert ediyor olsaydık şunları yapmamız gerekirdi.

$sqlQuery = "INSERT INTO `tbl_post` 
	     SET `title` = :title,
		  `content` = :content";
$pdo = new Pdo('mysql; host=127.0.0.1;dbname=mydb', 'dbuser', 'dbpass');
$statement = $pdo->prepare($sqlQuery);
$statement->bindValue(':title', 'sample post');
$statement->bindValue('content', 'post content');
$statement->execute();

Hakikaten kirli bir kod. Şimdi de gelin bu işi ActiveRecord ile yapalım.

$post = new Post();
$post->title = 'sample title';
$post->content = 'post content';
$post->save();

Bitti..! Temiz, güvenli ve stabil.

Güzelmiş… Peki ActiveRecord’u Nasıl Kullanırım ?

AR Tablolarımızla çatır çatır işlem yapabilmek için veritabanı bağlantımızın sular seller gibi açık olması gerekiyor. YII tarafında veritabanı bağlantısını başlangıçta ayağa kaldırmak için konfigürasyonda aşağıdaki değişikliği gerçekleştirmemiz gerekir.
protected/config/main.php:

return array(
    ......
    'components'=>array(
        ......
        'db'=>array(
            'class'=>'CDbConnection',
            'connectionString'=>'mysql:host=localhost;dbname=testdb',
            'username'=>'myusername',
            'password'=>'mypassword',
            'emulatePrepare'=>true,  // YII nin sitesinde bazi SQL sorgulari icin gerekli oldugundan bahsediyor. Muhtemelen parametre iceren SQL sorgularinin dogrudan calistirilmamasi icin kullanilmasi gereken bir parametre.
        ),
    ),
)

Bir de AR sınıflarımızın autoloader a tanıtılması gerekiyor ki PHP new Post() çağrısını aldığında “Post da kim?” demeisn. AR sınıflarımızın protected/models klasörü altında olduğunu varsayarak, konfigürasyonda aşağıdaki değişiklikleri yapmamız gerekir:
protected/config/main.php:

return array(
  ...... 
  'import'=>array(
      'application.models.*',
  ),
  ......
);

Şimdi de tablomuzu temsil edecek AR sınıfını kodlayalım.

class Post extends CActiveRecord
{
    public static function model($className=__CLASS__)
    {
        return parent::model($className);
    }
 
    public function tableName()
    {
        return 'tbl_post';
    }
}

Not: Eğer konfigürasyonda table prefix tanımlamışsanız tableName() metodu içinde tanımladığınız tablo adını ‘{{post}}’ şeklinde tanımlayabilirsiniz. Bu şekilde yapılan tablo tanımlaması konfigrasyondaki table prefix bilgisinden dolayı otomatik olarak tbl_post şeklinde kullanılacaktır. TablePrefix ile ilgili daha fazla bilgi için bu sayfa yı nceleyebilirsiniz.

AR Sınıflarını Kullanarak INSERT İşlemi Gerçekleştirmek:

Girişde gördüğünüz örnekden anlaşılacağı üzere her bir tablo bir sınıfla, tablodaki her bir alan sınıfın attribute u olarak temsil edilir. Veritabanına yeni bir kayıt oluşturmak için bir adet AR sınıfı instance oluşturup, alanlarınıza verilerinizi eşitledikten sonra save() metodunu çağırmanız yeterlidir. Oluşan yeni kaydın kimliği, primarykey olan field attribute una geri döner. Muhteşem, değil mi ? :)

$post = new Post();
$post->title = 'Sample post';
$post->content = 'Post content';
$post->save();

echo $post->id;

Olur da herhangibir alana veritabanı fonksiyonlarından birinin döndüğü bir değeri set etmek isterseniz :

$post->create_time = CDbExpression('Now()');

AR Sınıflarını Kullanarak SELECT İşlemi Gerçekleştirmek:

Arama işlemleri AR base sınıfı (CActiveRecord) üzerinden gerçekleştirilir. AR sınıflarının base sınıfına ulaşmak için model() metodunu kullanacağız. Aşağıda çeşitli arama şekillerini görüyoruz.

// Belirtilen şarta uygun ilk satırı döner.
$post=Post::model()->find($condition,$params);

// Belirtilen birincil anahtar a sahip (PrimaryKey) kaydı döner.
$post=Post::model()->findByPk($postID,$condition,$params);

// Belirtilen niteliklere uygun ilk satırı döner.
$post=Post::model()->findByAttributes($attributes,$condition,$params);

// SQL cümlesinde verilen şarta uygun kayıtlardan ilk satırı döner.
$post=Post::model()->findBySql($sql,$params);

// Belirtilen şarta uyan tüm satırları döner.
$posts=Post::model()->findAll($condition,$params);

// Belirtilen birincil anahtara uygun tüm kayıtları döner. Pratikte birincil anahtarın tek kayıt ile eşleşmesi beklenir.
$posts=Post::model()->findAllByPk($postIDs,$condition,$params);

//Belirtilen niteliklerle eşleşen tüm kayıtları döner.
$posts=Post::model()->findAllByAttributes($attributes,$condition,$params);

//Belirtilen SQL cümlesine uyan tüm kayıtları döner.
$posts=Post::model()->findAllBySql($sql,$params);

Aslanım ActiveRecord seviyoruz seni. Yukarıda gördüğünüz herbir findXXX metodu arma işleminin gerçekleştiği AR sınıfının bir örneğini yada AR sınıflarından oluşan bir kayıt dizisini döner. Gelin hemen birkaç örnek inceleyelim:

$post = Post::model()->find('id=:id', array(':id' => 10);
echo $pos->id;

condition parametresini isterseniz array(‘id’=>’:id’), isterseniz ‘id=:id’ yok ben öyle de istemiyorum derseniz aşağıdaki şekilde gönderebilirsiniz:

$condition = new CDbCriteria();
$condition->select='title, content';
$condition->condition='id=:id';
$condition->params = array(':id' => 10);

$post = Post::model()->find($condiiton); //Bulursak Post tipinde, bulamazsak null veri döner.

Oldu da birden fazla kayıt dönerse:

$posts = Post::model()->findAll('category_id=:category_id', array(':category_id' => 1));
foreach($posts as $post) {
  echo $post->title;
}

Bir de primary key e göre arayalım.

$post = Post::model()->findByPdk(10);
echo $post->title;

Bir de böyle count’lu exists li işler için aşağıdaki metodlarımız var.

// Belirtilen kriterlere uygun kayit sayisini doner.
$n=Post::model()->count($condition,$params);

// Belirtilen SQL cümlesine uyan kayıtların sayısını döner.
$n=Post::model()->countBySql($sql,$params);

// Belirtilen şartlara uygun kayıt olma durumu ile ilgili mantıksal (boolean:true/false) değer döner.
$exists=Post::model()->exists($condition,$params);

AR Sınıflarını Kullanarak UPDATE İşlemi Gerçekleştirmek:

Update işlemi insert işlemi ile aynıdır. Kaydı bulur, alanlara ilgili verileri yazar, save metodunu çağırırız.

$post = Post::model()->findByPk(1);
$post->title = 'Bu bir denemedir';
$post->save();

Şayet birden fazla kaydı güncellemek istiyorsanız aşağıdaki metodları kullanabilirsiniz:

// belirtilen şartla eşleşen tüm kayıtları günceller.
Post::model()->updateAll($attributes,$condition,$params);

// Belirtilen şartlar ve primary key ile eşleşen tüm kayıtları günceller.
Post::model()->updateByPk($pk,$attributes,$condition,$params);

// Bunu ben de yazarken öğrendim. YII de sayaç olarak atadiginiz alanlari 
// arttirmak için (Örneğin makaleyi okuyan kişi sayısı gibi) saveCounter() 
// ve updateCounter() gibi komutlar varmış. saveCounters(), sadece geçerli AR
// sınıfının sayaç alanlarını güncellerken, updateCounters() metodu birden çok 
// kaydın sayıcılarını güncelleyebiliyormuş.
// Daha detaylı bilgi için bu yazıyı inceleyebilirsiniz: 
// http://www.yiiframework.com/wiki/282/using-counters-with-activerecord/
Post::model()->updateCounters($counters,$condition,$params);

AR Sınıflarını Kullanarak DELETE İşlemi Gerçekleştirmek:

Insert ve Update işlemini gördükten sonra sanırım delete işleminin nasıl yapıldığını tahmin etmeniz çok zor olmamıştır. Kayıt silme işlemi için kaydı bulmamız ve ilgili kaydın AR sınıfından delete() metodunu çağırmamız yeterlidir.

//kaydımızı buluyoruz.
$post = Post::model()->findByPk(1);
//kaydımızı siliyoruz.
$post->delete()

Şayet birden fazla kaydı silmek istiyorsak ActiveRecord bize aşağıdaki metodları sunuyor.

// Belirtilen kriterlere uygun kayıtları siler.
Post::model()->deleteAll($condition,$params);

// Belirtilen birincil anahtar(lar) a uygun kayıt(lar) ı siler.
Post::model()->deleteByPk($pk,$condition,$params);

Bu yazıda temel işlemler dışında çok fazla detaya girmek istemedim. Detaylı ve güncel bilgi için http://www.yiiframework.com/doc/guide/1.1/en/database.ar adresinden yararlanabilirsiniz. ActiveRecord kullanarak ilişkili tablo yapılarında da çalışabilmeniz mümkündür. İlerleyen günlerde bu konuda da bir blog yazısı yayınlayacağım. Her türlü sorunuz veya öneriniz için ibrahimgunduz34(at)gmail.com adresinden bana ulaşabilirsiniz.