Pylons+memcacheDB...新規作成フォーム

まずは電話帳データを登録するフォームから作っていく。

phonebook/lib/helpers.py

from formbuild.helpers import field
from formbuild import start_with_layout as form_start, end_with_layout as form_end
from webhelpers.html.tags import *
from routes import url_for

Pylonsはフォーム部品の再利用も簡単。
phonebook/templates/derived/phone/fields.html

${h.field(
  "Mobile Number",
  h.text(name = 'mobile_number'),
  required = False,
)}

${h.field(
  "Name",
  h.text(name = "name"),
  required = True,
)}

${h.field(
  "Address",
  h.text(name = "address"),
  required = False,
)}

${h.field(
  "Home Number",
  h.text(name = "home_number"),
  required = False,
)}

${h.field(
  "DOB(yyyymmdd)",
  h.text(name = "dob"),
  required = False,
)}

phonebook/templates/derived/phone/new.html

<%inherit file="/base/index.html"/>
<%namespace file="fields.html" name="fields" import="*"/>

<%def name="heading()">
  <h1 class="main">Create a New Entry</h1>
</%def>

${h.form_start(h.url_for(controller = 'phone', action = 'create'), method = "post")}
  ${fields.body()}
  ${h.field(field = h.submit(value = "Create", name="submit"))}
${h.form_end()}

phonebook/controllers/phone.py

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

http://10.10.10.147:5000/phone/new にアクセス。

このままではデータベースに書けないので、Submitされたフォームを処理する部分を記述する。まずその前に、Pylonsにmemcachedbへのアクセス先を登録しておく必要がある。

開発用: Phobook/development.iniの[app:main]部分に以下を記述。

memcachedb.servers = 127.0.0.1:21201

テスト用: Phonebook/test.iniの[app:main]を以下のように記述。

[app:main]
use = egg:PhoneBook
full_stack = true
cache_dir = %(here)s/data
beaker.session.key = phonebook
beaker.session.secret = hogehogehoge

sqlalchemy.url = sqlite:///%(here)s/test.db
memcachedb.servers = 127.0.0.1:31201

次に、コントローラ(phone.py)を記述。以下のモジュールを取り込めるようにする。

# for form processing
import formencode
from formencode import htmlfill
import phonebook.model as model
from pylons.decorators.rest import restrict
from pylons.decorators import validate
import memcache
import jsonpickle
from pylons import config
import phonebook.lib.helpers as h

Pylonsのフォームチェックはデコレータを使う。そのデコレータに渡すフォームチェック用クラスを定義。(PhoneControllerの前で)

class NewPhoneForm(formencode.Schema):
    allow_extra_fields = True
    filter_extra_fields = True
    mobile_number = formencode.validators.String()
    name = formencode.validators.String(not_empty = True)
    address = formencode.validators.String()
    home_number = formencode.validators.String()
    dob = formencode.validators.String()

→nameが空の時だけ文句をいうようにした。

memcacheは本来memcached(キャッシュ)にアクセスするためのAPIだが、memcachedbはそのmemcachedAPIを「そのまま」使えるようになっている。そのmemcachedbにアクセスするハンドラを確保。(PhoneControllerの中で)

    def __init__(self):
        BaseController.__init__(self)
        self.mc = memcache.Client([config['app_conf']['memcachedb.servers']], debug = 0)

サブミットされたフォームデータを処理する部分。HTTP GETでのアクセスを拒否し、NewPhoneFormで定義されたフォーム内容チェックが走るようになっている。

    @restrict('POST')
    @validate(schema = NewPhoneForm(), form = 'new')
    def create(self):
        phone = model.Phone()
        for k, v in self.form_result.items():
            setattr(phone, k, v)

        p = jsonpickle.Pickler()

        self.mc.set(phone.id, p.flatten(phone))

        response.status_int = 302
        response.headers['location'] = h.url_for(controller = 'phone',
                                                 action = 'view',
                                                 id = phone.id)
        return "Moved temporarily"

書き込んだ後、viewに飛ばしている。そのviewもちゃんとmemcachedbから読み出すように変更しておく必要がある。

    def view(self, id):
        if id is None:
            abort(404)

        ser = self.mc.get(id.encode('ascii'))

        if ser is None:
            abort(404)

        u = jsonpickle.Unpickler()
        c.phone = u.restore(ser)

        if not isinstance(c.phone, model.Phone):
            abort(404)

        return render('/derived/phone/view.html')

templates/derived/phone/view.htmlも更新。

<%inherit file="/base/index.html"/>

<%def name = "title()">${c.phone.name}</%def>
<%def name = "heading()"><h1>${c.phone.name}</h1></%def>

Mobile:${c.phone.mobile_number}<br/>
Home: ${c.phone.address}<br/>
Address: ${c.phone.home_number}<br/>
DOB: ${c.phone.dob}<br/>

<%def name = "footer()">
<p>
 <a href="${h.url_for(controller = 'phone', action = 'list', id = None)}">All Pages</a>
|<a href="${h.url_for(controller = 'phone', action = 'new', id = None)}">New Page</a>
|<a href="${h.url_for(controller = 'phone', action = 'edit', id = c.phone.id)}">Edit Page</a>
|<a href="${h.url_for(controller = 'phone', action = 'delete', id = c.phone.id)}">Delete Page</a>
</p>

${parent.footer()}
</%def>

http://10.10.10.147:5000/phone/new のフォームに適当な入力をして投稿すると、memcachedbに登録されたデータが表示されるはず。

上記の場合、IDには「8636120904394d85b10b48260f81c4de」が割り当てられている。実際どうなっているか、memcachedbに直接つないで確認してみる。

 $ python
Python 2.6.4 (r264:75706, Dec 28 2009, 15:52:45)
[GCC 4.3.4] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import memcache
>>> mc = memcache.Client(['127.0.0.1:21201'], debug=0)
>>> v = mc.get('8636120904394d85b10b48260f81c4de')
>>> v
{'py/object': 'phonebook.model.Phone', 'name': u'masayang', 'dob': u'19200323', 'mobile_number': u'111-1111-1111', 'address': u'1400 Fashion Island', 'home_number': u'222-2222-2222', '_id': '8636120904394d85b10b48260f81c4de'}
>>>

json形式でシリアライズされたPhoneオブジェクトが格納されている。

Gitコミット先