読者です 読者をやめる 読者になる 読者になる

[[ともっくす alloc] init]

ともっくすの雑多な日記と技術的なメモ

Pyramidのチュートリアルをやってみる③ 〜 wikiアプリケーションの設計(ビューの定義)

Pyramidのチュートリアルをやってみる② 〜 wikiアプリケーションの設計(モデルの定義) - [[ともっくす alloc] init]の続き.

今回はwikiアプリケーションのビューの定義まで.

パッケージの依存関係

setup.py

今回のwikiアプリケーションでは,reStructuredTextマークアップを使えるようにするために,docutilsパッケージが必要となる.

そこで,setup.pyでこの依存関係をsetupメソッド内のrequiresパラメータに割り当てることによってdocutilsパッケージへの依存を追加する.

setup.py内のrequiresを以下のように変更する.

requires = [
    'pyramid',
    'SQLAlchemy',
    'transaction',
    'pyramid_tm',
    'pyramid_debugtoolbar',
    'zope.sqlalchemy',
    'waitress',
    'docutils',
    ]

setup.py developの実行

新たに追加された依存パッケージ(docutils)を登録,取得するために,

$ python setup.py develop

と,実行する.

views.py

views.pyを以下のように変更する.もはや,チュートリアルそのまま.

import re
from docutils.core import publish_parts

from pyramid.httpexceptions import (
    HTTPFound,
    HTTPNotFound,
    )
from pyramid.view import view_config

from .models import (
    DBSession,
    Page,
    )

# regular expression used to find WikiWords
wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)")

@view_config(route_name='view_wiki')
def view_wiki(request):
    return HTTPFound(location = request.route_url('view_page',
                                                  pagename='FrontPage'))

@view_config(route_name='view_page', renderer='templates/view.pt')
def view_page(request):
    pagename = request.matchdict['pagename']
    page = DBSession.query(Page).filter_by(name=pagename).first()
    if page is None:
        return HTTPNotFound('No such page')

    def check(match):
        word = match.group(1)
        exists = DBSession.query(Page).filter_by(name=word).all()
        if exists:
            view_url = request.route_url('view_page', pagename=word)
            return '<a href="%s">%s</a>' % (view_url, word)
        else:
            add_url = request.route_url('add_page', pagename=word)
            return '<a href="%s">%s</a>' % (add_url, word)

    content = publish_parts(page.data, writer_name='html')['html_body']
    content = wikiwords.sub(check, content)
    edit_url = request.route_url('edit_page', pagename=pagename)
    return dict(page=page, content=content, edit_url=edit_url)

@view_config(route_name='add_page', renderer='templates/edit.pt')
def add_page(request):
    pagename = request.matchdict['pagename']
    if 'form.submitted' in request.params:
        body = request.params['body']
        page = Page(pagename, body)
        DBSession.add(page)
        return HTTPFound(location = request.route_url('view_page',
                                                      pagename=pagename))
    save_url = request.route_url('add_page', pagename=pagename)
    page = Page('', '')
    return dict(page=page, save_url=save_url)

@view_config(route_name='edit_page', renderer='templates/edit.pt')
def edit_page(request):
    pagename = request.matchdict['pagename']
    page = DBSession.query(Page).filter_by(name=pagename).one()
    if 'form.submitted' in request.params:
        page.data = request.params['body']
        DBSession.add(page)
        return HTTPFound(location = request.route_url('view_page',
                                                      pagename=pagename))
    return dict(
        page=page,
        save_url = request.route_url('edit_page', pagename=pagename),
        )


まあ,なんとなくわかるかと.

で,各メソッドは以下のような役割を持つ.

view_wiki()
wiki自体を表示する
view_page()
個々のページを表示する
add_page()
ページの追加を可能にする
edit_page()
ページの編集を可能にする

詳しい説明は,ここを読めばわかるでしょう.
> ビューを定義する — The Pyramid Web Application Development Framework v1.4.1 (翻訳)

テンプレート

追加したview_page,add_page,edit_pageビューはtemplateを参照していて,これらのテンプレートはtemplatesディレクトリの中にある.

また,Chameleonテンプレートとして認識されるためには.pt拡張子でなければいけない.

PyramidはChameleonテンプレート以外にも,Makoテンプレートがサポートされている.
使い方等は,他の素晴らしいサイトを参照すべし.

view.pt

view.ptを以下のように変更.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal">
<head>
  <title>${page.name} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)</title>
  <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
  <meta name="keywords" content="python web application" />
  <meta name="description" content="pyramid web application" />
  <link rel="shortcut icon" href="${request.static_url('tutorial:static/favicon.ico')}" />
  <link rel="stylesheet" href="${request.static_url('tutorial:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
  <!--[if lte IE 6]>
  <link rel="stylesheet" href="${request.static_url('tutorial:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
  <![endif]-->
</head>
<body>
  <div id="wrap">
    <div id="top-small">
      <div class="top-small align-center">
        <div>
          <img width="220" height="50" alt="pyramid" src="${request.static_url('tutorial:static/pyramid-small.png')}" />
        </div>
      </div>
    </div>
    <div id="middle">
      <div class="middle align-right">
        <div id="left" class="app-welcome align-left">
          Viewing <b><span tal:replace="page.name">Page Name Goes Here</span></b><br/>
          You can return to the<a href="${request.application_url}">FrontPage</a>.<br/>
        </div>
        <div id="right" class="app-welcome align-right"></div>
      </div>
    </div>
    <div id="bottom">
      <div class="bottom">
        <div tal:replace="structure content">
          Page text goes here.
        </div>
        <p>
          <a tal:attributes="href edit_url" href="">
            Edit this page
          </a>
        </p>
      </div>
    </div>
  </div>
  <div id="footer">
    <div class="footer">&copy; Copyright 2008-2011, Agendaless Consulting.</div>
  </div>
</body>
</html>

edit.pt

edit.ptを以下のように変更.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal">
<head>
  <title>${page.name} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)</title>
  <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
  <meta name="keywords" content="python web application" />
  <meta name="description" content="pyramid web application" />
  <link rel="shortcut icon" href="${request.static_url('tutorial:static/favicon.ico')}" />
  <link rel="stylesheet" href="${request.static_url('tutorial:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
  <!--[if lte IE 6]>
  <link rel="stylesheet" href="${request.static_url('tutorial:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
  <![endif]-->
</head>
<body>
  <div id="wrap">
    <div id="top-small">
      <div class="top-small align-center">
        <div>
          <img width="220" height="50" alt="pyramid" src="${request.static_url('tutorial:static/pyramid-small.png')}" />
        </div>
      </div>
    </div>
    <div id="middle">
      <div class="middle align-right">
        <div id="left" class="app-welcome align-left">
          Editing <b><span tal:replace="page.name">Page Name Goes Here</span></b><br/>
          You can return to the
          <a href="${request.application_url}">FrontPage</a>.<br/>
        </div>
        <div id="right" class="app-welcome align-right"></div>
      </div>
    </div>
    <div id="bottom">
      <div class="bottom">
        <form action="${save_url}" method="post">
          <textarea name="body" tal:content="page.data" rows="10" cols="60"/><br/>
          <input type="submit" name="form.submitted" value="Save"/>
        </form>
      </div>
    </div>
  </div>
  <div id="footer">
    <div class="footer">&copy; Copyright 2008-2011, Agendaless Consulting.</div>
  </div>
</body>
</html>

ルートを追加

URLからコードへのマッピングをadd_route()メソッドで追加する.

以下のようなルートを追加する

/
view_wikiというルート名,つまりview_wikiビューにマッピング.トップページ.
/{pagename}
view_pageというルート名,つまりview_pageビューにマッピング.pagenameというタイトルのwikiページ.
/add_page/{pagename}
add_pageというルート名,つまりadd_pageビューにマッピング.pagenameというタイトルのwikiページを追加するためのページ.
/{pagename}/edit_page
edit_pageというルート名,つまりedit_pageビューにマッピング.pagenameというタイトルのwikiページを編集するためのページ.

__init__.py内のmainメソッドを:

def main(global_config, **settings):
    """ This function returns a Pyramid WSGI application.
    """
    engine = engine_from_config(settings, 'sqlalchemy.')
    DBSession.configure(bind=engine)
    Base.metadata.bind = engine
    config = Configurator(settings=settings)
    config.add_static_view('static', 'static', cache_max_age=3600)
    config.add_route('view_wiki', '/')
    config.add_route('view_page', '/{pagename}')
    config.add_route('add_page', '/add_page/{pagename}')
    config.add_route('edit_page', '/{pagename}/edit_page')
    config.scan()
    return config.make_wsgi_app()

と,変更する.

アプリケーション

起動

ここで一旦アプリケーションを起動してみる.

できません

…なんてことはありません.

はい,起動.

$ pserve development.ini --reload
Starting subprocess with file monitor
Starting server in PID 7139.
serving on http://0.0.0.0:6543

http://localhost:6543にアクセス.

f:id:o_tomox:20130721180545p:plain

ちゃんと,http://localhost:6543/FrontPageにリダイレクトされて,FrontPageの説明が載ってる.

この「This is the front page」っていうのは,initializedb.pyに記述した

model = Page('FrontPage', 'This is the front page')

の部分が反映されていることになる.

データの登録

次は,データを登録してみようと.

http://localhost:6543/add_page/Pyramidにアクセスすることで,Pyramidというwikiページを追加することができる.

テキストエリアの中に,

Pyramid is a *small*, *fast*, *down-to-earth* **Python web application development framework**. It is developed as part of the PylonsProject. It is licensed under a `BSD-like license
<http://repoze.org/license.html>`_.

と記述する.

こんな感じ.
f:id:o_tomox:20130721183402p:plain

で,saveすると,http://localhost:6543/Pyramidに飛ばされて,こんな感じのページに.
f:id:o_tomox:20130721182933p:plain

しっかりと,マークアップされている.


PylonsProjectのリンクは,PylonsProjectのwikiページを追加するためのページへのリンクになっている.
(BSD-like licenseのリンクは, BSD-like licenseの外部ページへのリンクで,reStructuredTextによるマークアップ

views.pyで,

r"\b([A-Z]\w+[A-Z]+\w+)"

という正規表現と,

def check(match):
    word = match.group(1)
    exists = DBSession.query(Page).filter_by(name=word).all()
    if exists:
        view_url = request.route_url('view_page', pagename=word)
        return '<a href="%s">%s</a>' % (view_url, word)
    else:
        add_url = request.route_url('add_page', pagename=word)
        return '<a href="%s">%s</a>' % (add_url, word)

というcheckメソッドを使っている.

この正規表現にマッチする語(大文字で始まり,途中にも大文字が現れるような文字列)がテキストにある場合,

  • テーブルに保存されていれば,そのwikiページへのリンク
  • テーブルに保存されていなければ,そのwikiページを追加するページへのリンク

に置き換えられる.

そのため,PylonsProjectという語にリンクが貼られている.


簡単にwikipediaもどきが作れた!



ということで,今回はこんな感じで.


次 > Pyramidのチュートリアルをやってみる④ 〜 wikiアプリケーションの設計(認証の追加) - [[ともっくす alloc] init]
前 > Pyramidのチュートリアルをやってみる② 〜 wikiアプリケーションの設計(モデルの定義) - [[ともっくす alloc] init]