Calmery.me

みっかぼうずにならないようがんばる

Pyramidでテンプレートを使う

Pyramid チュートリアル — Pyramid Tutorial for PyCon JP Sprint 1.0 documentation にテンプレートエンジンを使用して HTML の表示を行う項目があったのでやってみた.
このチュートリアルでは mako テンプレートというものを使うらしい.

makoテンプレートを使うために、 Configuratorにテンプレートが置かれているディレクトリを指定します。 まずは、テンプレート用のディレクトリを作成しましょう。

まずテンプレートを配置するディレクトリを作る.

$ mkdir templates

次にテンプレートとして使う index.mak を templates に作成する.

<html>
  <body>
    <h1>Hello, ${name}</h1>
  </body>
</html>

文中に ${ と } で囲まれた文字列があるが,

name 変数を表示するテンプレートです。 ${ と } で囲まれた部分は、pythonの式として評価されて、結果がHTML中に表示されます。

ということだ.結果をそのまま表示できるのは嬉しい.

次にこのテンプレートを表示させるための templating.py を作成する.

from waitress import serve
from pyramid.config import Configurator
from pyramid.view import view_config

@view_config(route_name="home", renderer="index.mak")
def index(request):
    return dict(name="pyramid")

if __name__ == '__main__':
    import os
    here = os.path.dirname(__file__)
    settings = {
        'mako.directories':[
            os.path.abspath(os.path.join(here, 'templates')),
        ],
    }
    config = Configurator(settings=settings)
    config.add_route('home', '/')
    config.scan()
    app = config.make_wsgi_app()
    serve(app, host='0.0.0.0')
$ python templating.py
serving on http://0.0.0.0:8080
...
ValueError: No such renderer factory .mak

表示されない.

pyramid_mako のインストール

調べてみると ここ に解決方法があった.

config = Configurator(settings=settings)
config.include('pyramid_mako') # < added that
config.add_route('home', '/')

pyramid_mako を include しろという.
ただ pyramid_mako なんてものは入っていないので pip を使って入れる.

$ pip install pyramid_mako
...
Successfully built pyramid-mako Mako MarkupSafe
Installing collected packages: MarkupSafe, Mako, pyramid-mako
Successfully installed Mako-1.0.4 MarkupSafe-0.23 pyramid-mako-1.0.2

動かしてみる.

$ python templating.py
serving on http://0.0.0.0:8080

localhost:8080 を見ると Hello, pyramid と表示された.

resource context

ビュー関数はあくまでビューなので、ここにロジックを書くのは推奨されません。 リソースを定義して、そちらにロジックを書きましょう。 また、リソースはリクエストごとに生成されたり、ロードされたりします。 リクエストに対応するリソースは、 context として、 request から取得できます。

処理はビューにベタ書きせずに別に書いてねってことみたい.
templating.py をチュートリアルに沿って編集する.

from datetime import datetime, date

from waitress import serve
from pyramid.config import Configurator
from pyramid.view import view_config
from pyramid.response import Response

class MyContext(object):
    def __init__(self, request):
        self.request = request

    def get_date(self):
        return date.today()

    def greeting(self):
        now = datetime.now()
        if now.hour < 12:
            return "Good morning"
        elif now.hour < 18:
            return "Good afternoon"
        else:
            return "Good evening"

@view_config(route_name="home", renderer="index.mak")
def index(request):
    return dict()

if __name__ == '__main__':
    import os
    here = os.path.dirname(__file__)
    settings = {
        'mako.directories':[
            os.path.abspath(os.path.join(here, 'templates')),
        ],
    }
    config = Configurator(settings=settings,
                          root_factory=MyContext)
    config.include('pyramid_mako') # < added that
    config.add_route('home', '/')
    config.scan()
    app = config.make_wsgi_app()
    serve(app, host='0.0.0.0')

index.mak の方にも変更を加える.

<html>
  <body>
    ${request.context.get_date()}
    ${request.context.greeting()}
  </body>
</html>

これだとオブジェクトを共有しちゃうじゃんと思ったら書いてあった.

route ごとに異なるリソースを使いたい場合は、 add_route のキーワード引数 resource_factory にクラスや関数を渡します。

add_route の引数 resource_factory に渡せばいいらしい.
と思ったら resource_factory という引数はない.ドキュメント を見てみると引数 factory に渡せば良さそう.

add_route(name, pattern=None, view=None, view_for=None, permission=None, factory=None, for_=None, header=None, xhr=None, accept=None, path_info=None, request_method=None, request_param=None, traverse=None, custom_predicates=(), view_permission=None, renderer=None, view_renderer=None, view_context=None, view_attr=None, use_global_views=False, path=None, pregenerator=None, static=False, **predicates)

以下を templatig.py に追加した.

class MyContext2(object):
    def __init__(self, request):
        self.request = request
    def hello(self):
        return "Hello World"

@view_config(route_name="test", renderer="test.mak")
def test(request):
    return dict()

# main
config.add_route('test', '/test', factory=MyContext2)

templates に test.mak を作成する.

<html>
  <body>
    ${request.context.hello()}
  </body>
</html>

home は MyContext を使い,test は MyContext2 を使う.
うまく分けることができた.