レンダラー¶
ビュー callbale は Response オブジェクトを 常に 必要とはしていません。 もしビューが返すものが Pyramid の Response インタフェースを実装していなければ、 Pyramid は renderer を使用してレスポンスを構築しようと試みます。 例えば:
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 インタフェースを実装していませんので、この例は失敗だと思うかもしれません。
しかし、 renderer
が view 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.JSONP
は JSONP
レンダラーファクトリーのヘルパーです。
このヘルパーはハイブリッドな 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_config
や pyramid.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 configuration で renderer
引数を使う場合、
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
andExpires
headers in the returned response. The same can also be achieved by returning various values in theresponse_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 theinfo
object fed to its constructor. These renderer factories are registered with aname
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 theinfo
object fed to its constructor. These renderer factories are registered with aname
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.