Renderers¶
A view callable needn't always return a Response object. If a view happens to return something which does not implement the Pyramid Response interface, Pyramid will attempt to use a renderer to construct a response. For example:
1from pyramid.view import view_config
2
3@view_config(renderer='json')
4def hello_world(request):
5 return {'content':'Hello!'}
The above example returns a dictionary from the view callable. A dictionary
does not implement the Pyramid response interface, so you might believe that
this example would fail. However, since a renderer
is associated with the
view callable through its view configuration (in this case, using a
renderer
argument passed to view_config()
), if the view
does not return a Response object, the renderer will attempt to convert the
result of the view to a response on the developer's behalf.
Of course, if no renderer is associated with a view's configuration, returning anything except an object which implements the Response interface will result in an error. And, if a renderer is used, whatever is returned by the view must be compatible with the particular kind of renderer used, or an error may occur during view invocation.
One exception exists: it is always OK to return a Response object, even when
a renderer
is configured. In such cases, the renderer is bypassed
entirely.
Various types of renderers exist, including serialization renderers and renderers which use templating systems.
Writing View Callables Which Use a Renderer¶
As we've seen, a view callable needn't always return a Response object. Instead, it may return an arbitrary Python object, with the expectation that a renderer will convert that object into a response instance on your behalf. Some renderers use a templating system, while other renderers use object serialization techniques. In practice, renderers obtain application data values from Python dictionaries so, in practice, view callables which use renderers return Python dictionaries.
View callables can explicitly call
renderers, but typically don't. Instead view configuration declares the
renderer used to render a view callable's results. This is done with the
renderer
attribute. For example, this call to
add_view()
associates the json
renderer
with a view callable:
config.add_view('myproject.views.my_view', renderer='json')
When this configuration is added to an application, the
myproject.views.my_view
view callable will now use a json
renderer,
which renders view return values to a JSON response serialization.
Pyramid defines several Built-in Renderers, and additional renderers can be added by developers to the system as necessary. See Adding and Changing Renderers.
Views which use a renderer and return a non-Response value can vary non-body
response attributes (such as headers and the HTTP status code) by attaching a
property to the request.response
attribute. See
Varying Attributes of Rendered Responses.
As already mentioned, if the view callable associated with a view configuration returns a Response object (or its instance), any renderer associated with the view configuration is ignored, and the response is passed back to Pyramid unchanged. For example:
1from pyramid.response import Response
2from pyramid.view import view_config
3
4@view_config(renderer='json')
5def view(request):
6 return Response('OK') # json renderer avoided
Likewise for an HTTP exception response:
1from pyramid.httpexceptions import HTTPFound
2from pyramid.view import view_config
3
4@view_config(renderer='json')
5def view(request):
6 return HTTPFound(location='http://example.com') # json renderer avoided
You can of course also return the request.response
attribute instead to
avoid rendering:
1from pyramid.view import view_config
2
3@view_config(renderer='json')
4def view(request):
5 request.response.body = 'OK'
6 return request.response # json renderer avoided
Built-in Renderers¶
Several built-in renderers exist in Pyramid. These renderers can be
used in the renderer
attribute of view configurations.
Note
Bindings for officially supported templating languages can be found at Available Add-On Template System Bindings.
string
: String Renderer¶
The string
renderer renders a view callable result to a string. If a view
callable returns a non-Response object, and the string
renderer is
associated in that view's configuration, the result will be to run the object
through the Python str
function to generate a string.
Here's an example of a view that returns a dictionary. If the string
renderer is specified in the configuration for this view, the view will render
the returned dictionary to the str()
representation of the dictionary:
1from pyramid.view import view_config
2
3@view_config(renderer='string')
4def hello_world(request):
5 return {'content':'Hello!'}
The body of the response returned by such a view will be a string representing
the str()
serialization of the return value:
{'content': 'Hello!'}
Views which use the string renderer can vary non-body response attributes by
using the API of the request.response
attribute. See
Varying Attributes of Rendered Responses.
JSON Renderer¶
The json
renderer renders view callable results to JSON. By
default, it passes the return value through the json.dumps
standard library
function, and wraps the result in a response object. It also sets the response
content-type to application/json
.
Here's an example of a view that returns a dictionary. Since the json
renderer is specified in the configuration for this view, the view will render
the returned dictionary to a JSON serialization:
1from pyramid.view import view_config
2
3@view_config(renderer='json')
4def hello_world(request):
5 return {'content':'Hello!'}
The body of the response returned by such a view will be a string representing the JSON serialization of the return value:
{"content": "Hello!"}
The return value needn't be a dictionary, but the return value must contain
values serializable by the configured serializer (by default json.dumps
).
You can configure a view to use the JSON renderer by naming json
as the
renderer
argument of a view configuration, e.g., by using
add_view()
:
1config.add_view('myproject.views.hello_world',
2 name='hello',
3 context='myproject.resources.Hello',
4 renderer='json')
Views which use the JSON renderer can vary non-body response attributes by
using the API of the request.response
attribute. See
Varying Attributes of Rendered Responses.
Serializing Custom Objects¶
Some objects are not, by default, JSON-serializable (such as datetimes and other arbitrary Python objects). You can, however, register code that makes non-serializable objects serializable in two ways:
Define a
__json__
method on objects in your application.For objects you don't "own", you can register a JSON renderer that knows about an adapter for that kind of object.
Using a Custom __json__
Method¶
Custom objects can be made easily JSON-serializable in Pyramid by defining a
__json__
method on the object's class. This method should return values
natively JSON-serializable (such as ints, lists, dictionaries, strings, and so
forth). It should accept a single additional argument, request
, which will
be the active request object at render time.
1from pyramid.view import view_config
2
3class MyObject(object):
4 def __init__(self, x):
5 self.x = x
6
7 def __json__(self, request):
8 return {'x':self.x}
9
10@view_config(renderer='json')
11def objects(request):
12 return [MyObject(1), MyObject(2)]
13
14# the JSON value returned by ``objects`` will be:
15# [{"x": 1}, {"x": 2}]
Using the add_adapter
Method of a Custom JSON Renderer¶
If you aren't the author of the objects being serialized, it won't be possible
(or at least not reasonable) to add a custom __json__
method to their
classes in order to influence serialization. If the object passed to the
renderer is not a serializable type and has no __json__
method, usually a
TypeError
will be raised during serialization. You can change this
behavior by creating a custom JSON renderer and adding adapters to handle
custom types. The renderer will attempt to adapt non-serializable objects using
the registered adapters. A short example follows:
1from pyramid.renderers import JSON
2
3if __name__ == '__main__':
4 config = Configurator()
5 json_renderer = JSON()
6 def datetime_adapter(obj, request):
7 return obj.isoformat()
8 json_renderer.add_adapter(datetime.datetime, datetime_adapter)
9 config.add_renderer('json', json_renderer)
The add_adapter
method should accept two arguments: the class of the
object that you want this adapter to run for (in the example above,
datetime.datetime
), and the adapter itself.
The adapter should be a callable. It should accept two arguments: the object
needing to be serialized and request
, which will be the current request
object at render time. The adapter should raise a TypeError
if it can't
determine what to do with the object.
See pyramid.renderers.JSON
and Adding and Changing Renderers
for more information.
Added in version 1.4: Serializing custom objects.
JSONP Renderer¶
Added in version 1.1.
pyramid.renderers.JSONP
is a JSONP renderer factory helper which implements
a hybrid JSON/JSONP renderer. JSONP is useful for making cross-domain AJAX
requests.
Unlike other renderers, a JSONP renderer needs to be configured at startup time
"by hand". Configure a JSONP renderer using the
pyramid.config.Configurator.add_renderer()
method:
from pyramid.config import Configurator
from pyramid.renderers import JSONP
config = Configurator()
config.add_renderer('jsonp', JSONP(param_name='callback'))
Once this renderer is registered via
add_renderer()
as above, you can use
jsonp
as the renderer=
parameter to @view_config
or
pyramid.config.Configurator.add_view()
:
from pyramid.view import view_config
@view_config(renderer='jsonp')
def myview(request):
return {'greeting':'Hello world'}
When a view is called that uses a JSONP renderer:
If there is a parameter in the request's HTTP query string (aka
request.GET
) that matches theparam_name
of the registered JSONP renderer (by default,callback
), the renderer will return a JSONP response.If there is no callback parameter in the request's query string, the renderer will return a "plain" JSON response.
Javscript library AJAX functionality will help you make JSONP requests. For example, JQuery has a getJSON function, and has equivalent (but more complicated) functionality in its ajax function.
For example (JavaScript):
var api_url = 'http://api.geonames.org/timezoneJSON' +
'?lat=38.301733840000004' +
'&lng=-77.45869621' +
'&username=fred' +
'&callback=?';
jqhxr = $.getJSON(api_url);
The string callback=?
above in the url
param to the JQuery getJSON
function indicates to jQuery that the query should be made as a JSONP request;
the callback
parameter will be automatically filled in for you and used.
The same custom-object serialization scheme defined used for a "normal" JSON renderer in Serializing Custom Objects can be used when passing values to a JSONP renderer too.
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:
1from pyramid.view import view_config
2
3@view_config(name='gone', renderer='templates/gone.pt')
4def myview(request):
5 request.response.status = '404 Not Found'
6 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.
1from pyramid.response import Response
2
3def view(request):
4 request.response.set_cookie('abc', '123') # this has no effect
5 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
:
1def view(request):
2 request.response.set_cookie('abc', '123')
3 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
.
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
:
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 should conform to the
pyramid.interfaces.IRendererFactory
interface. It should be capable of
creating an object that conforms to the pyramid.interfaces.IRenderer
interface. A typical class that follows this setup is as follows:
1class RendererFactory:
2 def __init__(self, info):
3 """ Constructor: info will be an object having the
4 following attributes: name (the renderer name), package
5 (the package that was 'current' at the time the
6 renderer was registered), type (the renderer type
7 name), registry (the current application registry) and
8 settings (the deployment settings dictionary). """
9
10 def __call__(self, value, system):
11 """ Call the renderer implementation with the value
12 and the system value passed in as arguments and return
13 the result (a bytes or string object). The value is
14 the return value of a view. The system value is a
15 dictionary containing available system values
16 (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()
, where 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:
1from pyramid.view import view_config
2
3@view_config(renderer='amf')
4def myview(request):
5 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:
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 .jinja2
in
the renderer
attribute of a view configuration:
1from pyramid.view import view_config
2
3@view_config(renderer='templates/mytemplate.jinja2')
4def myview(request):
5 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.
Adding a Default 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:
config.add_renderer(None, 'mypackage.json_renderer_factory')
Changing an Existing Renderer¶
Pyramid supports overriding almost every aspect of its setup through its
Conflict Resolution mechanism. This
means that, in most cases, overriding a renderer is as simple as using the
pyramid.config.Configurator.add_renderer()
method to redefine the
template extension. For example, if you would like to override the json
renderer to specify a new renderer, you could do the following:
json_renderer = pyramid.renderers.JSON()
config.add_renderer('json', json_renderer)
After doing this, any views registered with the json
renderer will use the
new renderer.
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 in turn is
the name of a registered renderer. For example:
1from pyramid.events import subscriber
2from pyramid.events import NewRequest
3
4@subscriber(NewRequest)
5def set_xmlrpc_params(event):
6 request = event.request
7 if (request.content_type == 'text/xml'
8 and request.method == 'POST'
9 and not 'soapaction' in request.headers
10 and not 'x-pyramid-avoid-xmlrpc' in request.headers):
11 params, method = parse_xmlrpc_request(request)
12 request.xmlrpc_params, request.xmlrpc_method = params, method
13 request.is_xmlrpc = True
14 request.override_renderer = 'xmlrpc'
15 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.