pylons-20080818 + tw.forms + elixir: Create Read Update Delete มั้ง

ลองใช้ pylons, tw.forms และ elixir ลองทำ application ง่ายๆ ตาม http://pylonsbook.com/alpha1/simplesite_tutorial ดูก็ดัดแปลงนิดหน่อยๆ เลยอย่างจะเขียนรวมกันไว้ที่เดียว. อาจจะดูมั่วๆ บ้างไว้มี version ต่อไปค่อย post อีกทีแล้วกัน.

pylons (20080818) นี้เลือกได้เลยว่าให้สร้าง code ที่ support mako และ sqlalchemy ได้เลย แต่จะใช้ elixir และ tw.forms ก็ต้องมาแก้นิดๆ หน่อย. เอา elixir ก่อนแล้วกัน.

แก้ lib/base.py:


from pylons.controllers import WSGIController
from pylons.templating import render_mako as render

import elixir # เพิ่มนี้เข้ามา
class BaseController(WSGIController):
    def __call__(self, environ, start_response):
        try:
            return WSGIController.__call__(self, environ, start_response)
        finally:
            elixir.session.remove() # เพิ่มนี้เข้ามา

แก้ model/meta.py:

from sqlalchemy import MetaData
from sqlalchemy.orm import scoped_session, sessionmaker
__all__ = ['Session', 'metadata']
engine = None

แก้ model/__init__.py:

import sqlalchemy as sa
from sqlalchemy import orm
from simple1.model import meta
import elixir

sm = orm.sessionmaker(autoflush=True, autocommit=True)
elixir.session = orm.scoped_session(sm)
elixir.options_defaults.update({ 'shortnames': True })

def init_model(engine):
    elixir.metadata.bind = engine

from elixir import *
from entities import *

elixir.setup_all()

แล้วก็แก้ websetup.py:

"""Setup the simple1 application"""
import logging

from simple1.config.environment import load_environment

log = logging.getLogger(__name__)

def setup_app(command, conf, vars):
    """Place any commands to setup simple1 here"""
    load_environment(conf.global_conf, conf.local_conf)

    import elixir # ใส่ elixir ไปแทน
    elixir.metadata.create_all(checkfirst=True) # บรรทัดนี้ด้วย

ส่วนที่ใส่ไปข้างบนนี้ไม่ขึ้นกับ app นะครับ ต่อไปก็เติม entity ใน model สักหน่อย ชื่อ Person ข้างในก็มีแค่ first name กับ last name😛

แก้ model/entities.py:

from elixir import *
class Person(Entity):
    first_name = Field(String(1000))
    last_name = Field(String(1000))

ว่าด้วยเรื่อง elixir ก็เกือบหมดแล้ว ที่เหลือก็อยู่ใน controller เลย. เพื่อที่จะสร้าง model ก็ลองสร้าง database เลยสั่ง

paster setup-app development.ini

ต่อด้วย tw.forms โดยทั่วไปแล้วพอหลังลง package ก็ลงแก้ config/middleware.py: แต่มันยาว ก็แค่เพิ่มอะไรไปนิดๆ หน่อยๆ

...
from tw.api import make_middleware  # แก้ตรงนี้ เพิ่มเข้ามา
...
    # แก้ตรงนี้
    # CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares)
    app = make_middleware(app, {
        'toscawidgets.framework' : 'pylons',
        'toscawidgets.framework.default_view' : 'mako',
        'toscawidgets.middleware.inject_resources' : True,
        })
   ...

ว่าด้วยเรื่อง view (template)

แก้ lib/helpers.py

from webhelpers import *
from webhelpers.html.tags import *
from routes import url_for
from webhelpers.html import literal
from webhelpers.pylonslib import Flash as _Flash
from webhelpers import paginate
flash = _Flash()

สร้าง widgets ก่อนจะได้เขียน template สั้นๆ สร้าง widgets/person.py

from tw import forms
from tw.api import WidgetsList

forms.FormField.engine_name = 'mako'

class PersonForm(forms.TableForm):
    class fields(WidgetsList):
        first_name = forms.TextField()
        last_name = forms.TextField()
        submit = forms.SubmitButton(attrs = {'value': 'save'})

person_form = PersonForm(id = 'person_form')

สร้าง base template ใน template/base.html: ต่างจากใน tutorial นิดๆ ที่มี flash message ด้วย

## -*- coding: utf-8 -*-





    ${self.title()}
    ${self.head()}


    ${self.header()}
    ${self.tabs()}
    ${self.menu()}
    ${self.heading()}
    ${self.breadcrumbs()}
    ${self.flash_messages()}
    ${next.body()}
    ${self.footer()}





% if messages:
<ul id="flash-messages">
    % for message in messages:
	<li>${message}</li>
% endfor</ul>
% endif

Simple1

<a name="top"></a>



<h1>${c.heading or 'No Title'}</h1>


<a href="#top">Top ^</a>

แก้ template/person/list.html:




<h1 class="main">Person List</h1>
<ul id="titles">
% for person in c.paginator:
	<li>${person.first_name} ${person.last_name}
        [${h.link_to('view', h.url_for(controller='person', action='view', id=person.id))}]
        [${h.link_to('edit', h.url_for(controller='person', action='edit', id=person.id))}]
        [${h.link_to('delete', h.url_for(controller='person', action='delete', id=person.id), onclick="return confirm('Are you sure you want to delete #" + str(person.id) + "?');")}]</li>
% endfor</ul>


<a href="${h.url_for(controller='person', action='new', id=None)}">New person</a>

${parent.footer()}

แก้ template/person/new.html:




<h1 class="main">Add person</h1>

${h.literal(person_form(action=h.url_for(controller='person', action='create')))}

แก้ template/person/new.html:




<h1 class="main">Edit person info</h1>

${h.literal(person_form(action=h.url_for(controller='person', action='save')))}



  <a href="${h.url_for(controller='person', action='list')}">All people</a>
| <a href="${h.url_for(controller='person', action='edit', id=c.person.id)}">Edit person</a>
| <a href="${h.url_for(controller='person', action='delete', id=c.person.id)}">Delete person</a>

${parent.footer()}

แก้ template/person/view.html:


View person

<h1>View person</h1>
${c.person.first_name}
${c.person.last_name}



  <a href="${h.url_for(controller='person', action='list')}">All people</a>
| <a href="${h.url_for(controller='person', action='edit', id=c.person.id)}">Edit person</a>
| <a href="${h.url_for(controller='person', action='delete', id=c.person.id)}">Delete person</a>

${parent.footer()}

สุดท้ายแล้วมั้งก็สร้าง controller แก้ controller/person.py:

import logging

from pylons import request, response, session
from pylons import tmpl_context as c
from pylons.controllers.util import abort, redirect_to

from simple1.lib.base import BaseController, render
import simple1.model as model
from simple1.lib import helpers as h

log = logging.getLogger(__name__)

from simple1.widgets.person import person_form
from tw.mods.pylonshf import validate
from webhelpers import paginate

import elixir

from formencode import htmlfill

def get_person(id):
    if id is None:
        abort(404)
    person = model.Person.query.get(int(id))
    if person is None:
        abort(404)
    return person

class PersonController(BaseController):

    def index(self):
        return redirect_to(action='list')

    def list(self):
        page_number = request.params.get('page', 1)
        people = model.Person.query.all()
        c.paginator = paginate.Page(people, page = page_number)
        return render('/person/list.html')

    def new(self):
        return render('/person/new.html')

    @validate(form=person_form, error_handler='new')
    def create(self):
        if request.method != 'POST':
            about(500)
        person = model.Person()
        for k,v in self.form_result.items():
            setattr(person, k, v)
        elixir.session.flush()
        h.flash("added person")
        return redirect_to(action='index')

    def view(self, id):
        c.person = model.Person.query.get(int(id))
        return render('/person/view.html')

    def delete(self, id=None):
        person = get_person(id)
        person.delete()
        elixir.session.flush()
        h.flash("deleted person")
        #referer = request.headers.get('REFERER', '/')
        #return redirect_to(referer)
        return redirect_to(action='list')

    def edit(self, id=None):
        person = get_person(id)
        return htmlfill.render(render('/person/edit.html'), person.to_dict())

    @validate(form=person_form, error_handler='edit')
    def save(self, id=None):
        person = get_person(id)
        for k,v in self.form_result.items():
            setattr(person, k, v)
        elixir.session.flush()
        h.flash("edited person")
        return redirect_to(action='view')

ดูอนาถๆ กลับมาอ่านทีหลังไม่รู้รู้เรื่องเปล่า

อ้างอิงเพิ่ม:
http://toscawidgets.org/documentation/tw.forms/tutorials/pylons_one.html

https://www.knowledgetap.com/hg/webhelpers/file/2eb9f423aafb/webhelpers/pylonslib.py

ใส่ความเห็น

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / เปลี่ยนแปลง )

Twitter picture

You are commenting using your Twitter account. Log Out / เปลี่ยนแปลง )

Facebook photo

You are commenting using your Facebook account. Log Out / เปลี่ยนแปลง )

Google+ photo

You are commenting using your Google+ account. Log Out / เปลี่ยนแปลง )

Connecting to %s