demo の中で提供されるデモアプリケーションを使って Pyramid Layout が 動いているところを見てみましょう。
通常の Pyramid のやり方と同じです:
それではいくつかコードを見ていきましょう。
Pyramid Layout は、プロジェクトの中で使用できる設定ディレクティブと デコレータを定義しています。それらをコードの中にロードする必要があります。 デモでは etc/development.ini ファイルでこれを行っています:
pyramid.includes =
pyramid_debugtoolbar
pyramid_jinja2
pyramid_layout
development.ini エントリーポイントは demo/__init__.py の中で 開始します:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | from pyramid.config import Configurator
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
config = Configurator(settings=settings)
config.add_static_view('static', 'static', cache_max_age=3600)
config.add_route('home.mako', '/')
config.add_route('home.chameleon', '/chameleon')
config.add_route('home.jinja2', '/jinja2')
config.scan('.layouts')
config.scan('.panels')
config.scan('.views')
return config.make_wsgi_app()
|
これはすべて Configurator アクションです。ここでは各ビューの route を 登録しています。そして、登録のために demo/layouts.py, demo/panels.py, demo/views.py を scan します。
全体像から始めましょう: Layout によるグローバルな ルックアンドフィールです:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | from pyramid_layout.layout import layout_config
@layout_config(template='demo:templates/layouts/layout.mako')
@layout_config(
name='chameleon',
template='demo:templates/layouts/layout.pt'
)
@layout_config(
name='jinja2',
template='demo:templates/layouts/layout.jinja2'
)
class AppLayout(object):
def __init__(self, context, request):
self.context = context
self.request = request
self.home_url = request.application_url
self.headings = []
@property
def project_title(self):
return 'Pyramid Layout App!'
def add_heading(self, name, *args, **kw):
self.headings.append((name, args, kw))
|
@layout_config デコレータは Pyramid Layout から来たもので、 Layout を定義および登録することができます。この場合、 3つのデコレータを積み重ねています。それぞれのテンプレート言語に対して 1つずつ、3つの Layout を作っています。
ノート
最初の @layout_config は name を持っていません。そのため それはビューがどの Layout が欲しいか特に指定しなかった場合に 得られる Layout になります。
21-24 行目は、テンプレートとテンプレートロジックをお互いの近くに置いておく ことの概念を例示しています。すべてのビューで project_title を表示する 必要があります。それはグローバルなルックアンドフィールの main template の一部です。そのため、それぞれのビューがデータ/ロジック を持つのではなく、グローバルな契約の一部としてこのロジックを layout の中の 1つの場所に置いています。
次に、これが3つの Layout のうちの1つに対するテンプレートの中で 使われている場所を見ましょう。この場合は demo/templates/layouts/layout.mako の Mako テンプレートです:
<title>${layout.project_title}, from Pylons Project</title>
ここで、重要な概念と、ある重要なマジックを見ることができます: テンプレート にはトップレベルの変数 layout があります。これは Layout クラス のインスタンスです。
ZPT の人々にとっては、 demo/templates/layouts/layout.pt にある マスターテンプレートを見ると先頭に不思議なものがあることに気がつくかも しれません: metal:define-macro はありません。 Chameleon は テンプレートがトップレベルのマクロになることを許すので、 Pyramid Layout は自動的に全テンプレートを main_template という名前のマクロに結び付けます。
ビューはどのようにして Layout を使用する方法を知るのでしょうか。 見てみましょう。
デモアプリには、いくつかの非常に単純なビューがあります:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | from pyramid.view import view_config
@view_config(
route_name='home.mako',
renderer='demo:templates/home.mako'
)
@view_config(
route_name='home.chameleon',
renderer='demo:templates/home.pt',
layout='chameleon'
)
@view_config(
route_name='home.jinja2',
renderer='demo:templates/home.jinja2',
layout='jinja2'
)
def home(request):
lm = request.layout_manager
lm.layout.add_heading('heading-mako')
lm.layout.add_heading('heading-chameleon')
lm.layout.add_heading('heading-jinja2')
return {}
|
再び3つの積み重なったデコレータと1つのcallableがあります。 デコレータはすべて通常の Pyramid @view_config のものです。
2つ目のデコレータは demo/templates/home.pt にある Chameleon テンプレート を指しています:
<metal:block use-macro="main_template">
<div metal:fill-slot="content">
<!-- Main hero unit for a primary marketing message or call to action -->
${panel('hero', title='Chameleon')}
<!-- Example row of columns -->
<div class="row">
<p>${panel('headings')}</p>
<h2>User Info</h2>
<p>${panel('usermenu',
user_info={
'first_name': 'Jane',
'last_name': 'Doe',
'username': 'jdoe'}
)}</p>
</div>
</div>
</metal:block>
最初の行はテンプレートを Layout に opt into するものです。 home.jinja2 の中では、この行はこんな風になります:
{% extends main_template %}
これら両方について、 main_template が Pyramid Layout によって Pyramid レンダラーグローバル変数を通してテンプレートのグローバルな名前空間に 挿入されます。その後は、そのテンプレート言語用の通常のセマンティクスに なります。
views.py に戻りましょう。このビュー関数は Layout Manager を 取得しています。 Pyramid Layout は利便性のためにそれを request に格納 しています。 LayoutManager の主な役割は、現在の Layout の 値の get/set です。もちろん、それはこの関数の中で行っていることです。
その後、その関数は Layout インスタンス を取得して グローバルなルックアンドフィールに必要な何らかの状態を操作します。もちろん これは AppLayout クラスでもできたでしょうが、場合によっては別のビューが 見出しに対して異なる値を持っていることがあります。
main template は、その中に興味深いものを持っています:
<body>
${panel('navbar')}
<div class="container">
${next.body()}
<hr>
<footer>
${panel('footer')}
</footer>
</div> <!-- /container -->
<!-- Le javascript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="${request.static_url('demo:static/js/jquery-1.8.0.min.js')}"></script>
<script src="${request.static_url('demo:static/js/bootstrap.min.js')}"></script>
</body>
ここで、グローバルな Layout を Panel によって再利用可能な部分に分解 しています。これらはどこから来たのでしょうか。 @panel_config デコレータです。それは panels.py の中で見ることができます。 例えばこれは:
${panel('navbar')}
...ここから来ています:
@panel_config(
name='navbar',
renderer='demo:templates/panels/navbar.mako'
)
def navbar(context, request):
def nav_item(name, url):
active = request.current_route_url() == url
item = dict(
name=name,
url=url,
active=active
)
return item
nav = [
nav_item('Mako', request.route_url('home.mako')),
nav_item('Chameleon', request.route_url('home.chameleon')),
nav_item('Jinja2', request.route_url('home.jinja2'))
]
return {
'title': 'Demo App',
'nav': nav
}
@panel_config は、 navbar という名前で Panel を登録しました。 その後、テンプレートはそれを使用、 またはオーバーライド することが できます。
home.mako ビューテンプレートにはさらに面白い Panel があります:
${panel('hero', title='Mako')}
...これは以下を呼び出します:
1 2 3 4 5 6 | @panel_config(
name='hero',
renderer='demo:templates/panels/hero.mako'
)
def hero(context, request, title='Hello, world!'):
return {'title': title}
|
これは、 Panel をパラメータ化して、他の場所で異なる方法で利用 できるということを示しています。