theduke hari 1

Apakah ada cara untuk belajar pemrograman selain dengan membuat sebuah
project? oleh karena itu setelah membaca specifikasi wsgi, maka saya
memutuskan untuk membuat sebuah blog engine project dengan menggunakan
python dan wsgi.

blog engine ini akan saya namankan project ‘theduke’, bukan duke
maskot Java, akan tetapi theduke, diilhami oleh nama anak buah Jet
dari film animasi Avatar.

Senjata yang digunakan:

  1. werkzeug

    Kenapa harus werkzeug?, tidak ada alasn khusus, saya hanya
    tertarik dengan namanya saja.Tentu saja, ada calon kuat yang lain
    diantaranya adalah webob, akan tetapi saya melihat werkzeug lebih
    sederhana daripada webob/paste.

  2. sqlalchemy

    Kalo yang ini tidak harus ditanyakan.

  3. jinja

    Tutorial werkzeug banyak menggunakan Jinja, jadi saya pilih Jinja
    saja.

Ada pertanyaan, kenapa tidak menggunakan framework yang sudah jadi
saja, seperti django? hmm, django adalah framework yang bagus, tapi
untuk project ini saya cenderung lebih ke antiframework. werkzueg
sendiri berfungsi seperti glue (lem) yang menghubungkan antara model
dan view. Dan juga sepertinya untuk newbie seperti saya, lebih baik
belajar dari bawah (dalam hal ini lebih ke wsgi).

Untuk project theduke, paling tidak ada beberapa model dasar, yaitu:

  • post
  • author
  • tag
  • comment

dimana hubungan antara model-model tersebut antara lain:

  • one-to-many : author -> post
  • one-to-many : post ->comment
  • many-to-many: post ->tag

berikut ini adalah module database yang menggambarkan table, object,
dan mapper antara table dan object.

#table author
author_table = Table('authors',metadata,
                     Column('id',Integer,primary_key=True),
                     Column('username',String(255),nullable=False,unique=True),
                     Column('password',String(255)),
                     Column('email',String(255),nullable=False,unique=True))

#table post
post_table = Table('posts',metadata,
                   Column('id',Integer,primary_key=True),
                   Column('author_id',Integer,ForeignKey('authors.id')),
                   Column('content',Text),
                   Column('title',String(255)),
                   Column('slugs',String(255),unique=True),
                   Column('excerpt',Text),
                   Column('status',Integer),
                   Column('comment_status',Integer(3)),
                   Column('created',DateTime,default=datetime.datetime.now()),
                   Column('updated',DateTime,default=datetime.datetime.now(),onupdate=datetime.datetime.now()))

#table comment
comment_table = Table('comments',metadata,
                      Column('id',Integer,primary_key=True),
                      Column('author_name',String(255),nullable=False),
                      Column('author_email',String(255),nullable=False),
                      Column('author_url',String(255),nullable=False),
                      Column('author_content',Text),
                      Column('post_id',Integer,ForeignKey('posts.id')))

#table tag
tag_table = Table('tags',metadata,
                  Column('id',Integer,primary_key=True),
                  Column('tag_string',String(255),nullable=False),
                  Column('slugs',String(255),nullable=False))

#table for many to many post and tag
post_tag_table = Table('post_tag',metadata,
                       Column('post_id',Integer,ForeignKey('posts.id')),
                       Column('tag_id',Integer,ForeignKey('tags.id')))

class Author(object):
    pass

class Post(object):
    def __init__(self,title,content,excerpt="",status=0,comment_status=0):
        self.title = title
        self.content = content
        self.excerpt = excerpt
        self.status = status
        self.comment_statut = comment_status
        self.slug_title()

    def slug_title(self):
        self.slugs = self.title.replace(" ","-")


class Comment(object):
    pass

class Tag(object):
    def __init__(self,tag_string):
        self.tag_string = tag_string
        self.create_slugs()

    def create_slugs(self):
        if not self.tag_string:
            raise Exception

        self.slugs = self.tag_string.replace(' ','-')



mapper(Author,author_table,
       properties={'posts':relation( Post, backref='author' )})

#one to many with comments
#many to many with tags
mapper(Post,post_table,
       properties={'comments':relation(Comment, backref='post'),
                   'tags':relation(Tag,secondary=post_tag_table,backref='posts')})

mapper(Comment,comment_table)
mapper(Tag,tag_table)

saya juga membuat sebuah fungsi test

def test(db_uri='sqlite:///test.db'):
    """
    This is just for testing
    """
    engine = create_engine(db_uri)
    metadata.drop_all(bind=engine)
    metadata.create_all(bind=engine)
    Session = sessionmaker(bind=engine)

    #create post
    p = Post("this is a hello world","Hello world")

    t = Tag("tag")
    t1 = Tag("tag2")

    p.tags.append(t)
    p.tags.append(t1)

    session = Session()
    session.save(p)
    session.commit()

    #qtag = session.query(Tag)
    #qtag = session.query(Post)
    return session

dan test langsung dari python console

 c = theduke.database.test()
 d = c.query(theduke.database.Tag)

langkah selanjutnya? Routing

mengenal WSGI

Tentang WSGI

Apa itu WSGI

wsgi::
Web Server Gateway Interface

untuk membuat standard interface antara web aplikasi yang dibuat
dengan python dengan web server.
WSGI mempunyai dua sisi:

  1. sisi server
  2. aplikasi / framework
  3. WSGI pada dasarnya hanya sebuah referensi interface yang harus
    diimplementasi baik oleh server maupun oleh applikasi, sehingga
    sebuah applikasi yang mengimplementasi wsgi dapat di-deploy,
    diserver mana saja asalkan server itu juga mengimplementasi WSGI
    interface.

    Ini dapat dibandingkan dengan dunia java, dimana banyak terdapat web
    framework, akan tetapi web framework itu bisa diapplikasikan di java
    web server mana saja, karena baik web framework maupun java web
    server mengimplentasi JavaEE interface yang salah satunya adalah
    servlet.

    Cara Kerja WSGI

    Server berhubungan dengan applikasi dengan memanggil sebuah callable
    (baik itu fungsi maupun object) yang disediakan oleh
    applikasi. callable itu mempunyai dua buah argumen yaitu environ dan
    start_response. Environ berupa python dictionary sedangkan
    start_response berupa fungsi.

    Sedangkan applikasi berhubungan dengan server, dengan mengirimkan
    header (dapat diartikan sebagai http header), dan memanggil
    start_response dan memberi argumen http response dan http header,
    kemudian mengembalikan list python yang berisi response. Agak
    membingunkan bila cuma membaca spesikasi, akan lebih mudah bila kita
    melihat contohnya

     def application(environ, start_response):
        "Contoh Sederhana dari WSGI Application"
        status = "200 OK"
        headers = [("Content-type","plain/text")]
        start_response(status,headers)
        return ['Hello world !\n']
      
    

    dari contoh diatas, bisa kita lihat bagaimana applikasi berhubungan
    dengan server, dimana applikasi mengirimkan status dan header dengan
    menggunakan fungsi start_response, dan mengembalikan body response
    kepada server.

    Middleware

    Yang dimaksud dengan middleware, adalah sebuah komponen python, yang
    mempunyai dua fungsi sebagai server, sekaligus sebagai
    applikasi. Pada awalnya memang membuat bingung, bagaimana bisa
    sebuah component dapat berfungsi sebagai server sekaligus sebagai
    aplikasi?.
    Jadi bila sebuah komponen berfungsi sebagai middleware, maka dia
    bertindak sebagai penengah/buffer/pembungkus dari applikasi
    lain. Jadi server tidak akan memanggil fungsi dari wsgi dari
    aplikasi,akan tetapi memanggil fungsi wsgi dari komponen middleware,
    yang nantinya akan memanggil fungsi wsgi dari applikasi.
    Middleware itu sendiri bisa merubah keluaran dari applikasi,
    melakukan routing dan berbagai macam hal lainnya.
    Contoh code dari Middleware

    class Upperware:
       def __init__(self, app):
          self.wrapped_app = app
    
       def __call__(self, environ, start_response):
          for data in self.wrapped_app(environ, start_response):
             return data.upper()
    
      
    

    contoh diatas , akan merubah keluaran dari aplikasi yang dibungkus
    middleware, menjadi huruf besar semua.

    contoh penggunaan:

      wrapped_app = Upperware(simple_app)
      serve(wrapped_app)
      
    

    Isi dari Environ

    Environ sendiri adalah sebuah dictionary yang berisi key dan value
    yang berasal dari CGI environment.

    berikut ini beberapa key yang terdapat pada environ:

    REQUEST_METHOD

    HTTP method, dapat berupa post atau get ataupun HTTP method lainya.
    SCRIPT_NAME

    PATH_INFO

    QUERY_STRING

    Query String

    CONTENT_TYPE

    CONTENT_LENGTH

    SERVER_NAME, SERVER_PORT

    SERVER_PROTOCOL

    HTTP_ Variables

    referensi
    http://python.org/dev/peps/pep-0333/
    WSGI Gateway or Glue