Skip to content

May 28, 2009

16

Django Dersleri – 3: Google App Engine & Django

.

Google App Engine ilk çıktığında sadece Python desteklemekteydi. Fakat birkaç ay önce Java’ da desteklemeye baÅŸladı. Umarız ilerleyen zamanlarda diÄŸer dilleri de destekler. Bu servis açık kaynak yazılım geliÅŸtirmek isteyenler için bulunmaz bir nimet. Özellikle Django‘nun büyüsüne kapılıp sonra da onu çalıştıracak ücretisiz :D bir sunucu bulamayan bizler için. Her sunucu django desteklemediÄŸi için ve django destekli sunucuların mailyetinin fazla olduÄŸu için isteÄŸiniz biraz kaçabilir. Ama Google App Engine bu konuda bize yardımıcı oluyor. Åžimdi App Engine sunucumuzda nasıl django çalıştırabileceÄŸimize bakalım. Burada bazı farklılıklarla karşılaÅŸacağız. Bunlardan en önemlisi modellerdeki farklılıklar. Django’nun django.db.models.Model nesnesini kullanmıyoruz. Onun yerine google.appengine.ext.db.Model nesnesini kullanıyoruz. Metotlar birbirine yakın olduÄŸu için alışmamız uzun sürmez. Bir de Gql diye birÅŸey var. SQL’in Googlecası diyebiliriz. Bu kadar konuÅŸtuktan sonra kodlamaya geçebiliriz.

Bu kısma geçmeden önce App Engine kurulumu ile alakalı ön bilgi almak için şuradaki yazıyı okumanızı öneririm.

Klasik bir blog uygulaması yapalım.

django-blog isminde bir dizin oluşturup içine app.yaml dosyamızı oluşturalım. İçeriği şöyle olsun

application: django-blog
version: 1
runtime: python
api_version: 1

handlers:
- url: /.*
script: main.py

Ardından app.yaml dosyasının yanına main.py dosyasını oluşturuyoruz. İçeriği şöle olsun

import os, sys

from django.conf import settings
settings._target = None

os.environ["DJANGO_SETTINGS_MODULE"] = "blog.settings"
sys.path.append("E:\\app_engine\\django-blog")

from google.appengine.ext.webapp import util
from django.conf import settings

settings._target = None

import django.core.handlers.wsgi
import django.core.signals
import django.db
import django.dispatch.dispatcher

django.dispatch.dispatcher.disconnect(django.db._rollback_on_exception,django.core.signals.got_request_exception)

def main():
    application = django.core.handlers.wsgi.WSGIHandler()
    util.run_wsgi_app(application)

if __name__ == "__main__":
    main()

Burada 7. satırı biraz önce oluşturduğumuz django-blog dizininin yolunu yazıyoruz. 6. satır ise birazdan oluşturacağımız projenin adı. Yani django-admin.py startproject komutuyla oluşturacağımız proje.

Komut penceresini açıp django-blog dizinine geldikten sonra blog isminde bir django projesi başlatıyoruz.

django-admin.py startproject blog

Şimdi çalışıp çalışmadığını görmek için sunucumuzu çalıştırıyoruz.

dev_appserver.py django-blog/

Şimdi http://localhost:8080/ adresinden Congrulations sayfasını görüyoruz. Artık django çalışıyor. Eğer göremiyorsanız başa dönüp tekrar gözden geçirin.

Komut penceresinden django-blog/blog dizinine gelip blogum isminde bir uygulama başlatıyoruz.

python manage.py startapp blogum

Bu komutla birlikte blogum isminde bir dizin oluşacak. Bu dizinin içindeki models.py dosyasına modelimizi tanımlayalım.

from google.appengine.ext import db

class Yazi(db.Model):
yazan = db.UserProperty()
metin = db.StringProperty(multiline=True)

Modelde yazan ve metin olmak üzere iki elaman tanımladık.

Djangonun en sevdiğin kısmı olan urls.py dosyasını da şöyle yapıyoruz:

from django.conf.urls.defaults import *
from blog.blogum.views import index,ekle

urlpatterns = patterns('',
(r'^blog/$',index),
(r'^blog/ekle/$',ekle),
)

Burada /blog adresinde index fonksiyonunu ve /blog/ekle adresinde de ekle fonksiyonunu çağıracağını bilidiriyoruz. Buradaki urlpatterns değeri düzenli ifadeler(regular expressions ) kullanılarak oluşturuluyor. Düzenli ifadeler hakkında bilgi edinmek için şuraya ve şuraya bakabilirsiniz.

Aynı dizindeki views.py dosyasına da olayları yazıyoruz:

from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render_to_response
from google.appengine.ext import db
from google.appengine.api import users
from blog.blogum.models import Yazi

def index(request):
    yazilar = Yazi.all()
    yazilar = yazilar.fetch(1000)
    if users.get_current_user():
        url = users.create_logout_url(request.path)
        url_text = "Cikis"
    else:
        url = users.create_login_url(request.path)
        url_text = "Giris"
    sablon_param = {"yazilar":yazilar,"url":url,"url_text":url_text}
    return render_to_response("index.html",sablon_param)

def ekle(request):
    metin = request.POST["metin"]
    yazi = Yazi()
    yazi.metin = metin
    if users.get_current_user():
        yazi.yazan = users.get_current_user()
    yazi.put()
    return HttpResponseRedirect("/blog")

Burada iki fonksiyonumuz var biri index biri de ekle, index fonksiyonunda yazilari veritabanından okuyoruz. İlk 1000 yazıyı çekiyoruz.  1000 yazmamızın nedeni ise veritabanından veri okurken en fazla 1000 adet okuyabiliyoruz. Bu sınırlama App Engine tarafından yapılıyor.

users.get_current_user() fonksiyonu ile kullanıcının giriş yapıp yapmadığına bakıyoruz. Buradaki kullanıcı girişinden kasdımız Gmail hesabıyla giriştir. Lokalden çalıştırırken buraya giriş yapıp herhangi bir mail adresi yazabiliyorsunuz. Ama uygulamayı App Engine sunucularına yükledikten sonra Gooogle hesabıyla giriş yapılıyor.

Django’nun vazgeçilmezlerinden biri olan sablonları kullanıyoruz. Sablona parametrelerini girdikten sonra render_to_response komutuyla sablonu render ediyoruz.

ekle fonksiyonunda ise POST metoduyla gelen veriyi okuyoruz. EÄŸer kullanıcı giriÅŸ yaptıysa yazinin yazarı oluyor. EÄŸer yapmadıysa yazi.yazan deÄŸeri boÅŸ oluyor. Bunu da ÅŸablonumuzda sorgulatarak “anonim” olarak belirtiyoruz. Yani kullanıcı giriÅŸ yapmadan yazı yazarsa yazan kısmı anonim olarak görünüyor. put() fonksiyonuyla veritabanına yazdığımız veriyi ekliyoruz. Son olarak “/blog” sayfasına yönlendiriyoruz.

Şablon kullanmak için şablon dizinlerini djangoya belirtmemiz gerekiyor. Bunun için django-blog/blog/settings.py dosyasına bir ekleme yapacağız. Bu dosyada TEMPLATE_DIRS değişkenine şablon dosyamızın yolunu ekliyoruz. Ben djang0-blog/blog/blogum dizinin içine sablon isminde bir oluşturup index.html dosyasını oraya koydum. O zaman değişikliği şöyle yapıyoruz:

TEMPLATE_DIRS = (
"e:\\app_engine\\django-blog\\blog\\blogum\\sablon"
)

sablon dizinin içine index.html dosyasını oluşturuyoruz.

<a href ="{{ url }}">{{ url_text }}</a>
<h1>Blog</h1>
{% for yazi in yazilar %}
{% if yazi.yazan %}
{{ yazi.yazan.nickname }}
{% else %}
anonim
{% endif %}
<blockquote>{{ yazi.metin|escape }}</blockquote>
{% endfor %}

<form action="/blog/ekle/" method="post">
<textarea name="metin" rows="3" cols="40"></textarea>
<input type="submit" value="Gönder">
</form>

Burada kullanıcının giriÅŸ yapıp yapmadığına bakarak giriÅŸ veya çıkış linklerini koyuyoruz. Ardında sablonda belirttiÄŸimiz yazilar deÄŸerini bir for döngüsüyle okuyup ekrana yazdırıyoruz. OluÅŸturduÄŸumuz form ile “/blog/ekle/” adresine post metoduyla yazdığımız yazının gönderilmesini saÄŸlıyoruz.

Veritabanındaki verilerin tamamını silmek için sunucuyu şu komutla başlatıyoruz:

dev_appserver.py –clear_datastore django-blog/

Hepsi bu kadar, kolay gelsin…

Benzer yazılar:

16 Comments Post a comment
  1. Jun 12 2009

    tÅŸkler

  2. zeliha
    Mar 13 2010
  3. zeliha
    Mar 13 2010

    bu hatayı aldım yardımcı olur musunuz nerede hata yaptığımı bulamaıyorum

  4. admin
    Mar 17 2010

    Hangi hata

  5. yigit
    Jan 6 2011

    Traceback (most recent call last):
    File “/home/yigit/google_appengine/google/appengine/tools/dev_appserver.py”, line 3245, in _HandleRequest
    self._Dispatch(dispatcher, self.rfile, outfile, env_dict)
    File “/home/yigit/google_appengine/google/appengine/tools/dev_appserver.py”, line 3186, in _Dispatch
    base_env_dict=env_dict)
    File “/home/yigit/google_appengine/google/appengine/tools/dev_appserver.py”, line 531, in Dispatch
    base_env_dict=base_env_dict)
    File “/home/yigit/google_appengine/google/appengine/tools/dev_appserver.py”, line 2410, in Dispatch
    self._module_dict)
    File “/home/yigit/google_appengine/google/appengine/tools/dev_appserver.py”, line 2320, in ExecuteCGI
    reset_modules = exec_script(handler_path, cgi_path, hook)
    File “/home/yigit/google_appengine/google/appengine/tools/dev_appserver.py”, line 2218, in ExecuteOrImportScript
    script_module.main()
    File “/home/yigit/google_appengine/django-blog/main.py”, line 23, in main
    util.run_wsgi_app(application)
    File “/home/yigit/google_appengine/google/appengine/ext/webapp/util.py”, line 97, in run_wsgi_app
    run_bare_wsgi_app(add_wsgi_middleware(application))
    File “/home/yigit/google_appengine/google/appengine/ext/webapp/util.py”, line 115, in run_bare_wsgi_app
    result = application(env, _start_response)
    File “/home/yigit/google_appengine/lib/django/django/core/handlers/wsgi.py”, line 184, in __call__
    self.load_middleware()
    File “/home/yigit/google_appengine/lib/django/django/core/handlers/base.py”, line 29, in load_middleware
    mod = __import__(mw_module, {}, {}, [''])
    File “/home/yigit/google_appengine/google/appengine/tools/dev_appserver.py”, line 1295, in Decorate
    return func(self, *args, **kwargs)
    File “/home/yigit/google_appengine/google/appengine/tools/dev_appserver.py”, line 1945, in load_module
    return self.FindAndLoadModule(submodule, fullname, search_path)
    File “/home/yigit/google_appengine/google/appengine/tools/dev_appserver.py”, line 1295, in Decorate
    return func(self, *args, **kwargs)
    File “/home/yigit/google_appengine/google/appengine/tools/dev_appserver.py”, line 1847, in FindAndLoadModule
    description)
    File “/home/yigit/google_appengine/google/appengine/tools/dev_appserver.py”, line 1295, in Decorate
    return func(self, *args, **kwargs)
    File “/home/yigit/google_appengine/google/appengine/tools/dev_appserver.py”, line 1798, in LoadModuleRestricted
    description)
    File “/home/yigit/google_appengine/lib/django/django/contrib/sessions/middleware.py”, line 2, in
    from django.contrib.sessions.models import Session
    File “/home/yigit/google_appengine/google/appengine/tools/dev_appserver.py”, line 1295, in Decorate
    return func(self, *args, **kwargs)
    File “/home/yigit/google_appengine/google/appengine/tools/dev_appserver.py”, line 1945, in load_module
    return self.FindAndLoadModule(submodule, fullname, search_path)
    File “/home/yigit/google_appengine/google/appengine/tools/dev_appserver.py”, line 1295, in Decorate
    return func(self, *args, **kwargs)
    File “/home/yigit/google_appengine/google/appengine/tools/dev_appserver.py”, line 1847, in FindAndLoadModule
    description)
    File “/home/yigit/google_appengine/google/appengine/tools/dev_appserver.py”, line 1295, in Decorate
    return func(self, *args, **kwargs)
    File “/home/yigit/google_appengine/google/appengine/tools/dev_appserver.py”, line 1798, in LoadModuleRestricted
    description)
    File “/home/yigit/google_appengine/lib/django/django/contrib/sessions/models.py”, line 51, in
    class Session(models.Model):
    File “/home/yigit/google_appengine/lib/django/django/db/models/base.py”, line 30, in __new__
    new_class.add_to_class(‘_meta’, Options(attrs.pop(‘Meta’, None)))
    File “/home/yigit/google_appengine/lib/django/django/db/models/base.py”, line 169, in add_to_class
    value.contribute_to_class(cls, name)
    File “/home/yigit/google_appengine/lib/django/django/db/models/options.py”, line 53, in contribute_to_class
    setattr(self, ‘verbose_name_plural’, meta_attrs.pop(‘verbose_name_plural’, self.verbose_name + ‘s’))
    File “/home/yigit/google_appengine/lib/django/django/utils/functional.py”, line 42, in __wrapper__
    res = self.__func(*self.__args, **self.__kw)
    File “/home/yigit/google_appengine/lib/django/django/utils/translation/trans_real.py”, line 268, in gettext
    _default = translation(settings.LANGUAGE_CODE)
    File “/home/yigit/google_appengine/lib/django/django/utils/translation/trans_real.py”, line 197, in translation
    default_translation = _fetch(settings.LANGUAGE_CODE)
    File “/home/yigit/google_appengine/lib/django/django/utils/translation/trans_real.py”, line 180, in _fetch
    app = getattr(__import__(appname[:p], {}, {}, [appname[p+1:]]), appname[p+1:])
    AttributeError: ‘module’ object has no attribute ‘messages’

    Hatasını alıyorum. Lütfen yardım edin.
    Teşekkürler.

  6. admin
    Jan 7 2011

    http://code.google.com/intl/tr-TR/appengine/articles/ bu örneÄŸi inceleyebilirsin. Daha iyi bir uygulama…

  7. yigit
    Jan 8 2011

    Sanırım bir problem var ama kestiremedim.
    teşekkürler.

  8. admin
    Jan 8 2011

    python versiyon kac? Google app engine son versiyonu kurmayi dene

  9. yigit
    Jan 22 2011

    2.6.1 versiyonum.

  10. admin
    Jan 22 2011

    Google App Engine’nin son versiyonunu kurmayı dene. Åžurayı da inceleyebilirsin.

  11. yigit
    Jan 22 2011

    Son versiyonu var zaten. Neden böyle bir hata alıyorum anlamış değilim :(

  12. admin
    Jan 22 2011

    AttributeError: ‘module’ object has no attribute ‘messages’ hatasını veriyor. messages özelliği olmayan bir modül kullanmışsın. Yazdığın kodların hatalı kısmını gönderebilir misin?
    Yapmaya çalıştığın sey google app engine üzerinde django çalıştırmak değil mi?

  13. yigit Sadic
    Feb 23 2011

    hatamı şimdi fark ettim.
    settings.py ‘deki INSTALLED_APPS kısmındaki ‘django.contrib.messages’, kaldırınca çalıştı.

    Bir sorum olucak.
    Google app engine üzerindeki Django’da admin paneli kullanılabilir mi?
    Veri tabanı olarak istediğimiz veri tabanını kullanabilir miyiz?
    Teşekkürler

  14. admin
    Feb 23 2011

    Bildiğim kadarıyla django admin panelini kullanamıyoruz.
    Veritabanın olarak sadece GAE’nin kendi veritabanı sistemini kullanabiliyoruz. Zaten GAE üzerinde kullandığımız django-nonrel (nonrelational) yani iliÅŸkisel olmayan veritabanı kulanan django. Burada genel olarak django’nun templating sistemini ve view lerini kullanıyoruz. Yani burada kullandığımız sistemde MVC yapısının model ayağında farklılıklar var. Bunu da GAE’nin bir kısıtlaması olarak görebiliriz.

  15. yigit Sadic
    Feb 23 2011

    acaba size özel olarak ulaşmam mümkün mü? Çünkü bu konu hakkında gerçekten uzun uzun konuşmam gerekiyor.
    Bir projede django & google app engine kullanmak istiyorum. Teşekkürler.

    yigitsadic[at]gmail[dot]com

  16. admin
    Feb 23 2011

    Tabi ki

Share your thoughts, post a comment.

(required)
(required)

Note: HTML is allowed. Your email address will never be published.

Subscribe to comments