レンダラー

ビュー callbale は Response オブジェクトを 常に 必要とはしていません。 もしビューが返すものが Pyramid の Response インタフェースを実装していなければ、 Pyramidrenderer を使用してレスポンスを構築しようと試みます。 例えば:

1
2
3
4
5
from pyramid.view import view_config

@view_config(renderer='json')
def hello_world(request):
    return {'content':'Hello!'}

上記の例では、ビュー callable は 辞書 を返します。 辞書は Pyramid の Response インタフェースを実装していませんので、この例は失敗だと思うかもしれません。 しかし、 rendererview configuration を通してビュー callable に関連づけられていて (この場合は view_config() に渡された引数 renderer が使われます) ビューが Response オブジェクトを 返さない 場合、このレンダラーはビューの結果を開発者に代わってレスポンスに変換しようと試みます。

もちろん、ビュー設定に関連づけられたレンダラーが何もない状態で Response インタフェースを実装したオブジェクト以外のものを返せばエラーとなります。 もしレンダラーが使われるなら、ビューが返すものはレンダラーが扱える種類のものと互換でなければならず、そうでなければビューの呼び出しの間にエラーが発生します。

ひとつ例外があります: たとえ renderer が設定されていても、レスポンスオブジェクトを返すことは 常に 許可されます。 ビュー callbale がレンダラーが設定されたビューからレスポンスオブジェクトを返すなら、レンダラーは無視されます。

シリアライズするレンダラーや、テンプレートシステムを使うレンダラーなど、さまざまな種類のレンダラーがあります。 レンダラーを使うビュー Callable を書く も見てください。

レンダラーを使うビュー Callable を書く

すでに見てきたとおり、ビュー callable は常に Response オブジェクトを返す必要はありません。 代わりに、 renderer がレスポンスインスタンスに変換するであろう任意の Python オブジェクトを返してもよいのです。 いくつかのレンダラーは、テンプレートシステムを用います; 他のレンダラーは、オブジェクトシリアライズの技術を用います。

ビュー設定で renderer 属性を用いて、ビュー callable と関連づけられるレンダラーを切り替えることができます。 例として、 add_view() の呼び出しで json レンダラーをビュー callable に関連づけてみます:

1
config.add_view('myproject.views.my_view', renderer='json')

この設定がアプリケーションに追加されると、ビュー callable myproject.views.my_view は今後 json レンダラーを使うようになります。 レンダラーはビューの戻り値を JSON レスポンスにシリアライズします。

他のビルトインのレンダラーには、 辞書をレスポンスに変換するのに Chameleon テンプレート言語を使うレンダラーがあります。 必要ならば、開発者は追加のレンダラーを登録できます (Adding and Changing Renderers を参照) 。

request.response 属性のプロパティを設定することで、レンダラーを使う Response でない値を返すようなビューに様々な body でないレスポンス属性 (例えば、ヘッダーや HTTP ステータスコード等) を設定できます。 Varying Attributes of Rendered Responses を見てください。

view configuration に関連づけられた view callable がレスポンスオブジェクトを直接返す場合、 ビュー設定によって関連づけられたすべてのレンダラーは無視され、レスポンスは Pyramid に返されるまで変更されません。 例えば、ビュー callable が pyramid.response.Response のインスタンスを返す場合、どのレンダラーも使われることはありません。

1
2
3
4
5
6
from pyramid.response import Response
from pyramid.view import view_config

@view_config(renderer='json')
def view(request):
    return Response('OK') # json renderer avoided

HTTP exception レスポンスの場合も同様です:

1
2
3
4
5
6
from pyramid.httpexceptions import HTTPFound
from pyramid.view import view_config

@view_config(renderer='json')
def view(request):
    return HTTPFound(location='http://example.com') # json renderer avoided

もちろん、レンダリングする代わりに request.response 属性を返すこともできます:

1
2
3
4
5
6
from pyramid.view import view_config

@view_config(renderer='json')
def view(request):
    request.response.body = 'OK'
    return request.response # json renderer avoided

ビルトインのレンダラー

Pyramid にはいくつかのビルトインのレンダラーがあります。 これらのレンダラーは、ビュー設定の renderer 属性に設定できます。

string: 文字列レンダラー

string レンダラーは、ビュー callable の戻り値を文字列にレンダリングします。 ビュー callable がレスポンスでないオブジェクトを返し、かつ string レンダラーが ビュー設定によって関連づけられていた場合、戻り値は Python の str 関数で文字列化されます。 注意として、ビュー callable が Unicode オブジェクトを返す場合、それは str() されません。

以下は辞書を返すビューの例です。 string レンダラーがこのビューの設定に明記されていれば、このビューは戻り値の辞書をその str() 表現に変換します。

1
2
3
4
5
from pyramid.view import view_config

@view_config(renderer='string')
def hello_world(request):
    return {'content':'Hello!'}

このビューによって返されたレスポンスのボディは、戻り値の str() シリアライズによる文字列表現となります。

1
{'content': 'Hello!'}

文字列レンダラーを使うビューには、 request.response の API を使って、様々なボディでないレスポンス属性を設定することができます。 Varying Attributes of Rendered Responses を見てください。

JSON レンダラー

json レンダラーは、ビュー callable の戻り値を JSON にレンダリングします。 デフォルトでは、戻り値をそのまま標準ライブラリ関数 json.dumps に渡し、その結果を レスポンスオブジェクトでラップします。また、レスポンスの content-type は application/json に設定されます。

以下は辞書を返すビューの例です。 json レンダラーがこのビューの設定に明記されていれば、このビューは戻り値の辞書を JSON にシリアライズします。

1
2
3
4
5
from pyramid.view import view_config

@view_config(renderer='json')
def hello_world(request):
    return {'content':'Hello!'}

このビューのレスポンスボディは、戻り値のJSONシリアライズ結果の文字列表現になります。

1
'{"content": "Hello!"}'

戻り値は辞書である必要はありませんが、設定されたシリアライザでシリアライズ可能でなければいけません (デフォルトでは json.dumps) 。

Note

デフォルトの json レンダラーを上書きするために、追加の引数を渡すことができます。 詳細は pyramid.renderers.JSON および Adding and Changing Renderers を見てください。

ビュー設定の renderer 引数に json を指定することで、JSON レンダラーを使うようにビューを設定できます。 例えば add_view() を使って:

1
2
3
4
config.add_view('myproject.views.hello_world',
                name='hello',
                context='myproject.resources.Hello',
                renderer='json')

JSON レンダラーを使うビューには、 request.response 属性の API を使って、様々なbody でないレスポンス属性を設定できます。 Varying Attributes of Rendered Responses を見てください。

カスタムオブジェクトのシリアライズ

Pyramid では、カスタムオブジェクトのクラスに __json__ メソッドを定義すればそのオブジェクトを JSON シリアライズ可能にできます。 このメソッドはネイティブな JSON シリアライズ可能な値 (例えば整数、リスト、辞書、文字列、その他) を返すべきです。 このメソッドは、追加の引数をひとつ受け取れるべきです。引数 request は、レンダリング時点でのアクティブなリクエストオブジェクトです。

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

class MyObject(object):
    def __init__(self, x):
        self.x = x

    def __json__(self, request):
        return {'x':self.x}

@view_config(renderer='json')
def objects(request):
    return [MyObject(1), MyObject(2)]

# the JSON value returned by ``objects`` will be:
#    [{"x": 1}, {"x": 2}]

あなたがシリアライズ対象のオブジェクトの作者でないなら、そのオブジェクトをシリアライズするためにそれらのクラスにカスタムの __json__ メソッドを追加することはできない (あるいは、少なくとも合理的でない) かもしれません。 レンダラーに渡されたオブジェクトがシリアライズ可能な型ではなく、また __json__ メソッドもない場合、通常はシリアライズの過程で TypeError が送出されます。 この振る舞いは、カスタムの JSON レンダラーを作り、 カスタムの型をハンドルするアダプターを追加することで変更することができます。 このレンダラーは、シリアライズ不可能なオブジェクトを登録されたアダプターに適合させようと試みます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from pyramid.renderers import JSON

json_renderer = JSON()
def datetime_adapter(obj, request):
    return obj.isoformat()
json_renderer.add_adapter(datetime.datetime, datetime_adapter)

# then during configuration ....
config = Configurator()
config.add_renderer('json', json_renderer)

このアダプターはふたつの引数を取るべきです: シリアライズが必要なオブジェクトと request です。 request はレンダリング時点でのリクエストオブジェクトです。 アダプターは、オブジェクトにすべきことを決定できない場合は TypeError を送出すべきです。

詳細は pyramid.renderers.JSON および Adding and Changing Renderers を見てください。

Note

カスタムオブジェクトのシリアライズは Pyramid 1.4 で追加された機能です。

JSONP レンダラー

Note

この機能は Pyramid 1.1 で追加されました。

pyramid.renderers.JSONPJSONP レンダラーファクトリーのヘルパーです。 このヘルパーはハイブリッドな json/jsonp のレンダラーを実装しています。 JSONP はクロスドメイン AJAX リクエストの生成に有用です。

他のレンダラーと異なり、JSONP レンダラーは起動時に “手動での” 設定を必要とします。 JSONP レンダラーの設定は pyramid.config.Configurator.add_renderer() メソッドで行います:

from pyramid.config import Configurator

config = Configurator()
config.add_renderer('jsonp', JSONP(param_name='callback'))

上記のように一度このレンダラーが add_renderer() で登録されると、 @view_configpyramid.config.Configurator.add_view() での renderer= のパラメーターとして jsonp が使えるようになります:

from pyramid.view import view_config

@view_config(renderer='jsonp')
def myview(request):
    return {'greeting':'Hello world'}

JSONP レンダラーを使うビューが呼び出されたとき、

  • リクエストの HTTP クエリーストリング ( request.GET ) のパラメーターのうち JSONP レンダラーの param_name にマッチするもの (デフォルトでは callback) があれば、そのレンダラーは JSONP レスポンスを返します。
  • リクエストのクエリーストリングにコールバック用のパラメーターがなければ、 レンダラーは ‘プレーン’ な JSON レスポンスを返します。

JavaScript ライブラリの AJAX 機能は JSONP リクエストの生成を助けてくれるでしょう。 たとえば jQuery には getJSON 関数 や、それと等価 (ですが、より複雑) な機能を含む ajax 関数 があります。

以下は JavaScript の例です:

var api_url = 'http://api.geonames.org/timezoneJSON' +
              '?lat=38.301733840000004' +
              '&lng=-77.45869621' +
              '&username=fred' +
              '&callback=?';
jqhxr = $.getJSON(api_url);

jQuery の getJSON 関数に渡された url パラメーター内の文字列 callback=? はクエリが JSONP のリクエストとして扱われるようjQuery に指示します。 callback パラメーターは jQuery によって自動的に埋められ、使用されます。

カスタムオブジェクトのシリアライズ で、 “通常の” JSON レンダラー向けにカスタムオブジェクトをシリアライズする方法を述べました。 この方法は JSONP レンダラーでも同様に利用できます。

*.pt または *.txt: Chameleon テンプレートレンダラー

Chameleon テンプレートには、ふたつのビルトインのレンダラーがあります。

ビュー設定の renderer 属性が絶対パス、相対パスまたは asset specification であって、ファイル名の拡張子が .pt のときに Chameleon ZPT レンダラーが使われます。 ZPT テンプレートについてのより詳しい情報は Chameleon ZPT Templates を見てください。

ビュー設定の renderer 属性が絶対パスまたは asset specification であって、ファイル名の拡張子が .txt のときに Chameleon text レンダラーが使われます。 Chameleon text テンプレートについてのより詳しい情報は Templating with Chameleon Text Templates を見てください。

これらのレンダラーの振る舞いは同じです。 ただし、テンプレートのレンダリングに使われるエンジンのみが異なります。

renderer 属性にテンプレートのパス、または asset specification (たとえば myproject:templates/foo.pt または myproject:templates/foo.txt) が指定されたとき、ビューは Response オブジェクトか Python の 辞書 を返さなければなりません。 テンプレートに関連づけられたビュー callable が Python の辞書を返すとき、 テンプレートには辞書がキーワード引数として渡され、 レンダラー実装がテンプレートをレンダリングした結果をレスポンスに含めてユーザーに返します。 ビュー callable が Response オブジェクトでも辞書でもないものを返した場合、 エラーが送出されるでしょう。

テンプレートにキーワードが渡される前に、 ビューが返した辞書から生成されたキーワード引数にいくつかの引数が追加されます。 callable オブジェクト – ビューの定義に使われたオブジェクトなら何でも – が、テンプレートに渡されるキーワード引数の集合に view キーワードとして自動的に挿入されます。 ビュー callable がクラスであれば、 view キーワードはそのクラスのインスタンスになります。 それ以外にテンプレートに渡されるキーワードに追加されるのは renderer_name (ディレクティブの renderer 属性に指定された文字列)、 renderer_info (レンダラーに関連する情報を含むオブジェクト)、 context (テンプレートのレンダリングに使われるビューのコンテキストリソース)、 request (テンプレートのレンダリングに使われるビューに渡されたリクエスト) です。 request は、 Pyramid 1.3 以降は req でも利用できます。

以下は、Chameleon ZPT レンダラーを使うビュー設定の例です:

1
2
3
4
5
6
 # config is an instance of pyramid.config.Configurator

 config.add_view('myproject.views.hello_world',
                 name='hello',
                 context='myproject.resources.Hello',
                 renderer='myproject:templates/foo.pt')

以下は、Chameleon text レンダラーを使うビュー設定の例です:

1
2
3
4
 config.add_view('myproject.views.hello_world',
                 name='hello',
                 context='myproject.resources.Hello',
                 renderer='myproject:templates/foo.txt')

Chameleon レンダラーを使うビューには、 request.response 属性の API を使って、様々なレスポンス属性を設定できます。 Varying Attributes of Rendered Responses を見てください。

*.mak または *.mako: Mako テンプレートレンダラー

Mako テンプレートレンダラーは、Mako テンプレートを使ってビューをレンダリングします。 これが使われるとき、ビューは Response オブジェクトか Python の 辞書 を返されければなりません。 辞書の項目は、グローバルテンプレート空間で使われます。 ビュー callable が Response オブジェクトでも辞書でもないものを返した場合、エラーが送出されるでしょう。

Mako テンプレートを明示するために view configurationrenderer 引数を使う場合、 renderer の値には、設定値 mako.directories からの相対パス (たとえば some/template.mak) か、または asset specification (たとえば apackage:templates/sometemplate.mak) が指定できます。 Mako テンプレート内では、相対パスのファイル名または asset specification を用いて他の Mako テンプレートを継承できます。

以下は、相対パスによるビュー設定の例です:

1
2
3
4
5
6
 # config is an instance of pyramid.config.Configurator

 config.add_view('myproject.views.hello_world',
                 name='hello',
                 context='myproject.resources.Hello',
                 renderer='foo.mak')

Mako の場合の重要な注意として、上記の相対パス名 foo.mak はパッケージからの相対パスではなく、 設定ファイルの設定値 mako.directories で設定したディレクトリー (単数または複数) からの相対パスになります。

このレンダラーは asset specification の書式でも指定できます。 以下はこの書式を使った場合のビュー設定の例です:

1
2
3
4
 config.add_view('myproject.views.hello_world',
                 name='hello',
                 context='myproject.resources.Hello',
                 renderer='mypackage:templates/foo.mak')

上記の設定では、 mypackage パッケージの templates ディレクトリー以下にある foo.mak という名前のファイルが使われます。

Mako テンプレートレンダラーは、標準の設定 pyramid.reload_templates に加えて、追加の引数を取ることができます。 追加の Mako Template Render Settings については、 Environment Variables and .ini File Settings を見てください。

Varying Attributes of Rendered Responses

Before a response constructed by a renderer is returned to Pyramid, several attributes of the request are examined which have the potential to influence response behavior.

View callables that don’t directly return a response should use the API of the pyramid.response.Response attribute available as request.response during their execution, to influence associated response behavior.

For example, if you need to change the response status from within a view callable that uses a renderer, assign the status attribute to the response attribute of the request before returning a result:

1
2
3
4
5
6
from pyramid.view import view_config

@view_config(name='gone', renderer='templates/gone.pt')
def myview(request):
    request.response.status = '404 Not Found'
    return {'URL':request.URL}

Note that mutations of request.response in views which return a Response object directly will have no effect unless the response object returned is request.response. For example, the following example calls request.response.set_cookie, but this call will have no effect, because a different Response object is returned.

1
2
3
4
5
from pyramid.response import Response

def view(request):
    request.response.set_cookie('abc', '123') # this has no effect
    return Response('OK') # because we're returning a different response

If you mutate request.response and you’d like the mutations to have an effect, you must return request.response:

1
2
3
def view(request):
    request.response.set_cookie('abc', '123')
    return request.response

For more information on attributes of the request, see the API documentation in pyramid.request. For more information on the API of request.response, see pyramid.request.Request.response.

Deprecated Mechanism to Vary Attributes of Rendered Responses

Warning

This section describes behavior deprecated in Pyramid 1.1.

In previous releases of Pyramid (1.0 and before), the request.response attribute did not exist. Instead, Pyramid required users to set special response_ -prefixed attributes of the request to influence response behavior. As of Pyramid 1.1, those request attributes are deprecated and their use will cause a deprecation warning to be issued when used. Until their existence is removed completely, we document them below, for benefit of people with older code bases.

response_content_type
Defines the content-type of the resulting response, e.g. text/xml.
response_headerlist
A sequence of tuples describing header values that should be set in the response, e.g. [('Set-Cookie', 'abc=123'), ('X-My-Header', 'foo')].
response_status
A WSGI-style status code (e.g. 200 OK) describing the status of the response.
response_charset
The character set (e.g. UTF-8) of the response.
response_cache_for
A value in seconds which will influence Cache-Control and Expires headers in the returned response. The same can also be achieved by returning various values in the response_headerlist, this is purely a convenience.

Adding and Changing Renderers

New templating systems and serializers can be associated with Pyramid renderer names. To this end, configuration declarations can be made which change an existing renderer factory, and which add a new renderer factory.

Renderers can be registered imperatively using the pyramid.config.Configurator.add_renderer() API.

For example, to add a renderer which renders views which have a renderer attribute that is a path that ends in .jinja2:

1
config.add_renderer('.jinja2', 'mypackage.MyJinja2Renderer')

The first argument is the renderer name. The second argument is a reference to an implementation of a renderer factory or a dotted Python name referring to such an object.

Adding a New Renderer

You may add a new renderer by creating and registering a renderer factory.

A renderer factory implementation is typically a class with the following interface:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class RendererFactory:
    def __init__(self, info):
        """ Constructor: info will be an object having the
        following attributes: name (the renderer name), package
        (the package that was 'current' at the time the
        renderer was registered), type (the renderer type
        name), registry (the current application registry) and
        settings (the deployment settings dictionary). """

    def __call__(self, value, system):
        """ Call the renderer implementation with the value
        and the system value passed in as arguments and return
        the result (a string or unicode object).  The value is
        the return value of a view.  The system value is a
        dictionary containing available system values
        (e.g. view, context, and request). """

The formal interface definition of the info object passed to a renderer factory constructor is available as pyramid.interfaces.IRendererInfo.

There are essentially two different kinds of renderer factories:

  • A renderer factory which expects to accept an asset specification, or an absolute path, as the name attribute of the info object fed to its constructor. These renderer factories are registered with a name value that begins with a dot (.). These types of renderer factories usually relate to a file on the filesystem, such as a template.
  • A renderer factory which expects to accept a token that does not represent a filesystem path or an asset specification in the name attribute of the info object fed to its constructor. These renderer factories are registered with a name value that does not begin with a dot. These renderer factories are typically object serializers.

Here’s an example of the registration of a simple renderer factory via add_renderer():

1
2
3
# config is an instance of pyramid.config.Configurator

config.add_renderer(name='amf', factory='my.package.MyAMFRenderer')

Adding the above code to your application startup configuration will allow you to use the my.package.MyAMFRenderer renderer factory implementation in view configurations. Your application can use this renderer by specifying amf in the renderer attribute of a view configuration:

1
2
3
4
5
from pyramid.view import view_config

@view_config(renderer='amf')
def myview(request):
    return {'Hello':'world'}

At startup time, when a view configuration is encountered, which has a name attribute that does not contain a dot, the full name value is used to construct a renderer from the associated renderer factory. In this case, the view configuration will create an instance of an MyAMFRenderer for each view configuration which includes amf as its renderer value. The name passed to the MyAMFRenderer constructor will always be amf.

Here’s an example of the registration of a more complicated renderer factory, which expects to be passed a filesystem path:

1
2
config.add_renderer(name='.jinja2',
                    factory='my.package.MyJinja2Renderer')

Adding the above code to your application startup will allow you to use the my.package.MyJinja2Renderer renderer factory implementation in view configurations by referring to any renderer which ends in .jinja in the renderer attribute of a view configuration:

1
2
3
4
5
from pyramid.view import view_config

@view_config(renderer='templates/mytemplate.jinja2')
def myview(request):
    return {'Hello':'world'}

When a view configuration is encountered at startup time, which has a name attribute that does contain a dot, the value of the name attribute is split on its final dot. The second element of the split is typically the filename extension. This extension is used to look up a renderer factory for the configured view. Then the value of renderer is passed to the factory to create a renderer for the view. In this case, the view configuration will create an instance of a MyJinja2Renderer for each view configuration which includes anything ending with .jinja2 in its renderer value. The name passed to the MyJinja2Renderer constructor will be the full value that was set as renderer= in the view configuration.

Changing an Existing Renderer

You can associate more than one filename extension with the same existing renderer implementation as necessary if you need to use a different file extension for the same kinds of templates. For example, to associate the .zpt extension with the Chameleon ZPT renderer factory, use the pyramid.config.Configurator.add_renderer() method:

1
config.add_renderer('.zpt', 'pyramid.chameleon_zpt.renderer_factory')

After you do this, Pyramid will treat templates ending in both the .pt and .zpt filename extensions as Chameleon ZPT templates.

To change the default mapping in which files with a .pt extension are rendered via a Chameleon ZPT page template renderer, use a variation on the following in your application’s startup code:

1
config.add_renderer('.pt', 'mypackage.pt_renderer')

After you do this, the renderer factory in mypackage.pt_renderer will be used to render templates which end in .pt, replacing the default Chameleon ZPT renderer.

To associate a default renderer with all view configurations (even ones which do not possess a renderer attribute), pass None as the name attribute to the renderer tag:

1
config.add_renderer(None, 'mypackage.json_renderer_factory')

Overriding A Renderer At Runtime

Warning

This is an advanced feature, not typically used by “civilians”.

In some circumstances, it is necessary to instruct the system to ignore the static renderer declaration provided by the developer in view configuration, replacing the renderer with another after a request starts. For example, an “omnipresent” XML-RPC implementation that detects that the request is from an XML-RPC client might override a view configuration statement made by the user instructing the view to use a template renderer with one that uses an XML-RPC renderer. This renderer would produce an XML-RPC representation of the data returned by an arbitrary view callable.

To use this feature, create a NewRequest subscriber which sniffs at the request data and which conditionally sets an override_renderer attribute on the request itself, which is the name of a registered renderer. For example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
from pyramid.event import subscriber
from pyramid.event import NewRequest

@subscriber(NewRequest)
def set_xmlrpc_params(event):
    request = event.request
    if (request.content_type == 'text/xml'
            and request.method == 'POST'
            and not 'soapaction' in request.headers
            and not 'x-pyramid-avoid-xmlrpc' in request.headers):
        params, method = parse_xmlrpc_request(request)
        request.xmlrpc_params, request.xmlrpc_method = params, method
        request.is_xmlrpc = True
        request.override_renderer = 'xmlrpc'
        return True

The result of such a subscriber will be to replace any existing static renderer configured by the developer with a (notional, nonexistent) XML-RPC renderer if the request appears to come from an XML-RPC client.