ビュー

Pyramid と Pylons の間の最も大きな違いは、ビューがどのように構造化されるか、 そしてビューがどのようにテンプレートを起動して状態変数にアクセスするかです。 これは、テンプレート、レンダラー、リクエスト変数、 URL ジェネレータなどに 関係するため大きなトピックです。また、これらのトピックのうちのいくつかには 多くの側面があります。したがって、とにかく始めてそのまま進み、最終的に それを体系化することにします。

最初に Pylons のビューの取り扱いを確認しましょう。 Pylons では、ビュー は「アクション」と呼ばれ、コントローラクラスのメソッドです。 Pylons は コントローラのモジュール名、クラス名およびベースクラスに関する特有の 規則を持っています。 Pylons がルートに URL をマッチさせる場合、それは コントローラとアクションを調べるために routes の ‘controller’ と ‘action’ 変数を使用します。それはコントローラをインスタンス化し、アクションを 呼び出します。アクションは route の中のルーティング変数と同じ名前の 引数を受け取ることができます; Pylons は route から現在の値を渡します。 アクションは通常文字列を返します。これは通常はテンプレートをレンダリング するために render(template_name) を呼び出すことによって行われます。 あるいは、アクションは WebOb Response を返すこともできます。 リクエストの state データは、現在のリクエストに対する値を含んでいる 特殊なグローバル変数によって扱われます (これはリクエストパラメータ、 レスポンス属性、テンプレート変数、セッション変数、 URL ジェネレータ、 キャッシュオブジェクト、「アプリケーショングローバル」オブジェクトが 含まれます)。

ビュー関数とビューメソッド

Pyramid の ビュー callable は、関数またはメソッドです。また、それは 任意の場所に置くことができます。最も基本的な形式は、 request を受け取って response を返す関数です:

from pyramid.response import Response

def my_view(request):
    return Response("Hello, world!")

ビューメソッドは任意のクラスの中に置くことができます。ビューメソッドを 含むクラスは、慣例的に「ビュークラス」あるいは「ハンドラ」と呼ばれます。 ビューがメソッドの場合、 request はクラスコンストラクタに渡され、 メソッドは引数なしで呼ばれます。

1
2
3
4
5
6
class MyHandler(object):
    def __init__(self, request):
        self.request = request

    def my_view(self):
        return Response("Hello, classy world!")

Pyramid 構造には 3 つの大きな利点があります。

  • 最も重要なこととして、それはテストするのがより簡単です。ユニットテストは ダミーのリクエストでビューを呼び、テンプレートに渡されるはずの 辞書を受け取ることができます。それによって、データ変数を HTML からパース することなく直接調査することができます。
  • それは単純で、よりモジュール性が高いです。魔法のグローバル変数はありません。
  • ビューを好きなように構造化する自由があります。

典型的なビューの使用方法

単にビューを定義するだけでは、それを Pyramid に使用させるのに不十分です。 config.add_view() を呼ぶか @view_config デコレータを使用して、 ビューを登録しなければなりません。

ビューを使用する最も一般的な方法は @view_config デコレータを使う ことです。これは、 callable をビューとしてマークして、テンプレートを 指定できるようにします。さらに、ビュークラスによって共有される共通 コードのためのベースクラスを定義することも一般的です。以下は Akhet デモから借用したコードです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from pyramid.view import view_config

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

class Main(Handler):

    @view_config(route_name="home", renderer="index.mako")
    def index(self):
        return {"project": "Akhet Demo"}

アプリケーションの main 関数には config.scan() 行があり、 @view_config デコレータを探してすべてのアプリケーションモジュールを インポートします。 @view_config デコレータそれぞれに対して、それと同じ キーワード引数を伴って config.add_view(view) が呼ばれます。スキャナは さらに後で見る他のいくつかのデコレータも認識します。すべてのビューが特定の モジュールまたはサブパッケージに存在することを知っていれば、それだけを スキャンすることができます: config.scan(".views")

例における @view_config デコレータは、 2 つの引数 ‘route_name’ と ‘renderer’ を持っています。 URL ディスパッチを使用する場合、どの route がこのビューを起動すべきかを Pyramid に伝えるために ‘route_name’ 引数は 必須 です。 “renderer” 引数は、起動すべきテンプレートを指定します。 この場合、ビューの戻り値はテンプレート用データ変数の辞書です (これは Pylons の ‘c’ 変数に代わるもので、 TurboGears の使用パターンを模倣します)。 レンダラーは、レスポンスオブジェクトを生成する面倒を見ます。

ビュー設定引数

以下の引数を @view_config または config.add_view に渡すことが できます。クラスに含まれるすべてのビューでいくつかの引数の値が同じなら、 それらを 1 箇所で指定するために クラス に対して @view_defaults を使用することができます。

このリストは Pylons 風のアプリケーションで一般的に使用される引数だけを 含んでいます。完全なリストは Pyramid マニュアルの View Configuration にあります。引数には add_route 引数と同じく述語/非述語の区別があります。 異なる状況で異なるビューを起動するために、 1 つのルートに対して複数のビュー を各々異なる述語引数と共に登録することができます。

引数のうちいくつかは add_routeadd_view で共通です。 route のケースでは、 route が URL とマッチするかどうかを判断します。 ビューのケースでは、ビューがルートとマッチするかどうかを判断します。

route_name

[述語引数] このビューを取り付けるルート。 URL ディスパッチを使用する 場合は必須です。

renderer

[非述語引数] レンダラーまたはテンプレートの名前。下記のレンダラーの 項目で議論されます。

permission

[非述語引数] 現在のユーザーがビューを起動するために持っていなければ ならないパーミッションを指定する文字列。

http_cache

[非述語引数] レスポンスの ‘Expires’ および ‘Cache-Control’ HTTP ヘッダ に影響します。これは、レスポンスをキャッシュすべきかどうか、そしてそれは どれくらいの期間かをブラウザに伝えます。値は、キャッシュする秒数を 指定する整数、 datetime.timedelta インスタンス、あるいはキャッシュ することを防ぐ 0 のいずれかです。これはビューコード内で request.response.cache_expires(value) を呼ぶことと等価です。

context

[述語引数] context がこのクラスのインスタンスか、このインタフェース を実装する場合のみ、このビューが選ばれます。これはトラバーサル、認可 および例外ビューと共に使用されます。

request_method

[述語引数] 文字列 “GET”, “POST”, “PUT”, “DELETE’, “HEAD” のうちの 1 つ。 ビューが選ばれるためには、リクエストメソッドはこれと等しくなければ なりません。 REST アプリケーションは、しばしば同じルートに対して 異なるリクエストメソッドと共に複数のビューを登録します。

request_param

[述語引数] これは “foo” のような文字列にすることが可能で、その場合 ビューが選ばれるためにリクエストが “foo” という名前のクエリパラメータ または POST 変数を持っていなければならないことを示しています。あるいは 文字列が “foo=1” のように “=” を含んでいる場合、リクエストはこの パラメータを含んでいなければならず、 かつ その値は指定された値と 等しくなければなりません。そうでなければ、このビューは選ばれません。

match_param

[述語引数] request_param と似ていますが、 matchdict の中のルーティング 変数を参照します。 “foo” と “foo=1” 構文に加えて、キー/値ペアの辞書 を渡すことができます: これらのルーティング変数はすべて存在しなければ ならず、指定された値を持っていなければなりません。

xhr, accept, header, path_info

[述語引数] これらは config.add_route の対応する引数と同じように 機能します。

custom_predicates

[述語引数] 値は関数のリストです。それぞれの関数は contextrequest の引数を受け取り、ビューが引数を受理可能かどうかによって true または false を返します。すべての関数が true を返す場合のみビュー が選ばれます。関数引数が config.add_route の対応するオプションとは 異なることに注意してください。

URL ディスパッチと共に使用 しない ビューオプションの 1 つは “name” 引数です。これはトラバーサルの中でのみ使用されます。

レンダラー

レンダラー はビューのためのポストプロセッサです。それはビューの戻り値 をレスポンスに変換します。これにより、ビューの中で決まりきった boilerplate コードを避けることができます。 Pyramid は次に挙げるレンダラーを搭載して います: Mako, Chameleon, 文字列, JSON および JSONP 。 Mako と Chameleon レンダラーは、辞書を受け取り、指定されたテンプレートを起動し、レスポンスを 返します。文字列レンダラーは任意の型を文字列に変換します。 JSON と JSONP レンダラーは、任意の型を JSON または JSONP に変換します (Python の json シリアライザを使用します。それは、限定された種類の型を受け付けます)。

非テンプレートレンダラーは固定の名前を持っています: renderer="string", renderer="json", renderer="jsonp" 。 テンプレートレンダラーはテンプレートのファイル名拡張子によって起動されます。 そのため renderer="mytemplate.mako"renderer="mytemplate.mak" によって Mako が起動されます。 INI ファイルか main 関数で Mako 検索パスを 指定する必要があることに注意してください:

[app:main]
mako.directories = my_app_package:templates

Mako レンダラーに相対パスではなく asset スペックを渡すことができるはずで、 そうすれば Mako 検索パスの定義を避けることができますが、私はそれを動作 させることができませんでした (訳注: Pyramid 1.3.2 には Mako ルックアップに 関して不具合がある https://github.com/Pylons/pyramid/issues/606 )。 Chameleon テンプレートは .pt で終わり、必ず asset スペックとして指定されます。

他のテンプレートエンジン用にサードパーティのレンダラーを登録することが できます。また、異なるファイル名拡張子に対してレンダラーを再登録する こともできます。 Akhet デモには .html で終わるテンプレートを Mako を渡す ように Pyramid を設定する例があります。

また、ビューコードの内部でレンダラーを起動することができます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from pyramid.renderers import render, render_to_response

variables = {"dear": "Mr A", "sincerely": "Miss Z",
    "date": datetime.date.today()}

# Render a template to a string.
letter = render("form_letter.mako", variables, request=self.request)

# Render a template to a Response object.
return render_to_response("mytemplate.mako", variables,
    request=self.request)

ビューのデバッグ

選択されるはずだと思うルートまたはビューが選択されないという問題を 抱えている場合は、 development.ini の中で “pyramid.debug_notfound” と “pyramid.debug_routematch” のどちらかまたは一方を true に設定してみて ください。それによって推論結果がコンソールに記録されるようになります。

同じ callable を使用した複数のビュー

テンプレートが異なってもビューロジックが同じという場合には、同じビュー メソッドや関数上に複数の @view_config を積み重ねることができます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@view_config(route_name="help", renderer="help.mak")
@view_config(route_name="faq", renderer="faq.mak")
@view_config(route_name="privacy", renderer="privacy_policy.mak")
def template(request):
    return {}

@view_config(route_name="info", renderer="info.mak")
@view-config(route_name="info_json", renderer="json")
def info(request):
    return {}