とりあえず動かすまで
まず、pasteを使って、wsgiアプリケーションの土台を作る
paster create -t paste_deploy wsgiwiki
cd wsgiwiki
コンフィグのひな形をプロジェクトトップに移動。
mv docs/devel_config.ini .
とりあえず動かしてみる。
paster serve --reload devel_config.ini
ディスパッチャの導入とモデル作成
selectorを導入する。
wsgiwiki/wsgiapp.py
from paste.deploy.config import ConfigMiddleware
import selector
def make_app(
global_conf,
**kw):
app = selector.Selector()
conf = global_conf.copy()
conf.update(kw)
app = ConfigMiddleware(app, conf)
return app
Hello, worldを表示する。
wsgiwiki/wsgiapp.py
def index(environ, start_response):
start_response('200 OK', [('Content-type', 'text/html')])
return ['Hello, world.']
def make_app(....):
....
app.add('/', GET=index)
モデルを作る。
wsgiwiki/model.py
from elixir import *
class Page(Entity):
pagename = Field(Unicode, unique=True)
contents = Field(Unicode)
このモデルからテーブルを作る。(SQLiteを使う)
>>> from wsgiwiki.model import *
>>> metadata.bind = 'sqlite:///devdata.sqlite'
>>> metadata.bind.echo = True
>>> setup_all()
>>> create_all()
ついでに、初期データを投入
>>> page = Page(pagename='FrontPage', contents='This is FrontPage')
>>> page.flush()
コンフィグに接続文字列を設定する。
devel_config.ini
[app:devel]
dburi = sqlite:///%(here)s/devdata.sqlite
アプリケーション内でモデルを使う準備をする。
wsgiwiki/wsgiapp.py
from model import *
def make_app(...):
...
metadata.bind = kw['dburi']
setup_all()
Elixir0.5からは、setup_allを明示的に呼ぶようになりました。
ページ表示のwsgiアプリケーションを作成。
wsgiwiki/wsgiapp.py
def page(environ, start_response):
start_response('200 OK', [('Content-type', 'text/html')])
return ['Hello, world.']
マッピングする。
wsgiwiki/wsgiapp.py
def make_app(...):
...
app.add('/', GET=page)
app.add('/{pagename}', GET=page)
デフォルトの場合と、ページ名を指定した場合の2通りをマッピングする。
とりあえず、ページ名を表示させてみる。
wsgiwiki/wsgiapp.py
def page(environ, start_response):
start_response('200 OK', [('Content-type', 'text/html')])
pagename = environ.get('wsgiorg.routing_args')[1].get('pagename', 'FrontPage')
return [pagename]
モデルを取得して、そのページ名を表示させてみる。
wsgiwiki/wsgiapp.py
def page(environ, start_response):
start_response('200 OK', [('Content-type', 'text/html')])
pagename = environ.get('wsgiorg.routing_args')[1].get('pagename', 'FrontPage')
page = Page.get_by(pagename=pagename)
return [page.pagename]
tempitaで、テンプレートを使うようにする。
wsgiwiki/wsgiapp.py
def page(environ, start_response):
start_response('200 OK', [('Content-type', 'text/html')])
pagename = environ.get('wsgiorg.routing_args')[1].get('pagename', 'FrontPage')
page = Page.get_by(pagename=pagename)
template = tempita.HTMLTemplate.from_filename(os.path.join(os.path.dirname(__file__), 'page.html'))
return template.substitute(page=page)
テンプレート内で、とりあえずページ名を表示する。
wsgiwiki/page.html
{{page.pagename}}
ちゃんとしたxhtmlの体裁に整える。
wsgiwiki/page.html<?xml version="1.0"?>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" value="text/html;charset=utf-8" />
<title>{{page.pagename}}</title>
</head>
<body>
<h1>{{page.pagename}}</h1>
<a href="{{page.pagename}};edit">Edit</a>
<div>
{{page.contents}}
</div>
</body>
</html>
ページ編集と入力チェック
ページ編集のwsgiアプリを作る。
def edit_page(environ, start_response):
start_response('200 OK', [('Content-type', 'text/html')])
pagename = environ.get('wsgiorg.routing_args')[1].get('pagename', 'FrontPage')
page = Page.get_by(pagename=pagename)
template = tempita.HTMLTemplate.from_filename(os.path.join(os.path.dirname(__file__), 'edit_page.html'))
return template.substitute(page=page)
マッピングを追加する。
app.add('/{pagename};edit', GET=edit_page) # /{pagename} の前に書くこと!
次は、保存用のアプリケーション。
POSTパラメータ解析のため、webobを使う。
import webob
....
def save_page(environ, start_response):
request = webob.Request(environ)
pagename = environ.get('wsgiorg.routing_args')[1].get('pagename', 'FrontPage')
p = Page.get_by(pagename=pagename)
p.contents = request.params['contents']
p.flush()
return page(environ, start_response)
そして、マッピングを追加。
既存のものと同じURLだが、メソッドが異なる。
app.add('/{pagename}', GET=page, POST=save_page)
フォーマット変換に、markdownを導入する。
モデルのプロパティとして、変換したコンテンツを取得できるようにする。
@property
def htmlContents(self):
return markdown.markdown(self.contents)
変換したコンテンツを表示させる。
<div>
{{page.htmlContents}}
</div>
デフォルトでは、HTMLタグがエスケープされるため、htmlフィルタを追加する。
<div>
{{page.htmlContents | html}}
</div>
WIKIネームをリンクに変換する。
pagePattern = re.compile(r'\b([A-Z][a-z]+[A-Z]\w*)\b')
@property
def htmlContents(self):
html = markdown.markdown(self.contents)
html = pagePattern.sub(r'<a href="\1">\1</a>', html)
return html
まだ登録されていないページにアクセスしたときは、edit_pageに処理を渡す。
また、このときに、start_responseが二度呼ばれてしまうことを回避するため、start_response呼び出しを、return 直前に移動する。
def page(environ, start_response):
pagename = environ.get('wsgiorg.routing_args')[1].get('pagename', 'FrontPage')
page = Page.get_by(pagename=pagename)
if page is None:
return edit_page(environ, start_response)
template = tempita.HTMLTemplate.from_filename(os.path.join(os.path.dirname(__file__), 'page.html'))
start_response('200 OK', [('Content-type', 'text/html')])
return template.substitute(page=page, pagename=pagename)
まだ存在しないページを編集することになるため、pagenameはリクエストの内容をテンプレートに渡す。
contentsもpageがある場合だけ表示するようにする。
def edit_page(environ, start_response):
....
return template.substitute(page=page, pagename=pagename)
<?xml version="1.0"?>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" value="text/html;charset=utf-8" />
<title>{{pagename}}</title>
</head>
<body>
<h1>{{pagename}}</h1>
<form action="{{pagename}}" method="post">
<div>
<textarea name="contents">{{page.contents if page else ''}}</textarea>
<button type="submit">Save</button>
</div>
</form>
</body>
</html>
保存時に、pageがない場合は新規作成する。
def save_page(environ, start_response):
...
p = Page.get_by(pagename=pagename)
if p is None:
p = Page(pagename=pagename)
p.contents = request.params['contents']
空の内容を登録可能なため、FormEncodeで入力チェックをする。
ページ用のスキーマを作る。
wsgiwiki/schema.py
import formencode
import formencode.validators as validators
class PageSchema(formencode.Schema):
contents = validators.UnicodeString(not_empty=True)
入力エラーの場合は、編集画面に戻るため、edit_pageの引数で、エラー辞書を受け取れるようにする。
また、エラー辞書はそのままテンプレートに渡す。
def edit_page(environ, start_response, errors={}):
...
return template.substitute(page=page, pagename=pagename, errors=errors)
page取得前に、schemaで入力チェックをする。
エラーの場合は、エラー辞書とともに、edit_pageに委譲。def save_page(environ, start_response):
...
schema = PageSchema()
try:
params = schema.to_python(request.params)
except formencode.Invalid, e:
return edit_page(environ, start_response, e.error_dict)
edit_page.htmlテンプレート内で、エラーメッセージ表示をする。<?xml version="1.0"?>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" value="text/html;charset=utf-8" />
<title>{{pagename}}</title>
</head>
<body>
<h1>{{pagename}}</h1>
<form action="{{pagename}}" method="post">
<div>
<textarea name="contents">{{page.contents if page else ''}}</textarea>
{{if 'contents' in errors}}
<span class="error">{{errors['contents']}}</span>
{{endif}}
<button type="submit">Save</button>
</div>
</form>
</body>
</html>
That's all. Thanks!
Powered by ScribeFire.
|