TurboGears always provided builtin support for JSON Rendering, this is provided by the
JSONRenderer and the json.encode() function.
The first is what empowers the @expose('json') feature while the second is an
utility function you can call whenever encoding to json is needed. Both rely on
on tg.jsonify.JSONEncoder which is able to handle more types than the standard
one provided by the python json module and can be extended to support more types.
Using it is as simple as:
@expose('json')
def jp(self, **kwargs):
return dict(hello='World')
Which, when calling /jp would result in:
{"hello": "World"}
While you can create your own encoder, turbogears has a default instance of JSONEncoder
which is used for all encoding performed by the framework itself. Behavior of this encoder
can be driven by providing a __json__ method inside objects for which you want to
customize encoding and can be configured using AppConfig which supports the
following options:
json.isodates-> Whenever to encode dates in ISO8601 or not, the default isFalsejson.custom_encoders-> Dictionary oftype: functionmappings which can specify custom encoders for specific types. Custom encoders are functions that are called to get a basic object the json encoder knows how to handle.
For example to configure a custom encoder for dates your project app_cfg.py would look
like:
from datetime import date
def dmy_encoded_date(d):
return d.strftime('%d/%m/%Y')
base_config['json.custom_encoders'] = {date: dmy_encoded_date}
That would cause all datetime.date instances to be encoded using dmy_encode_date function.
If the encoded object provides a __json__ method this is considered the custom encoder
for the object itself and it is called to get a basic type the json encoder knows how to handle
(usually a dict).
Note
json.custom_encoders take precedence over __json__, this is made so that
users can override behavior for third party objects that already provide a __json__
method.
The same options available inside the json. configuration namespace are available
as render_params for the expose decorator. So if you want to turn
on/off iso formatted dates for a single method you can do that using:
from datetime import datetime
@expose('json', render_params=dict(isodates=True))
def now(self, **kwargs):
return dict(now=datetime.utcnow())
Since version 2.3.2 TurboGears provides built-in support for JSONP rendering.
JSONP works much like JSON output, but instead of providing JSON response it provides
an application/javascript response with a call to a javascript function providing
all the values returned by the controller as function arguments.
To enable JSONP rendering you must first append it to the list of required engines
inside your application config/app_cfg.py:
base_config.renderers.append('jsonp')
Then you can declare a JSONP controller by exposing it as:
@expose('jsonp')
def jp(self, **kwargs):
return dict(hello='World')
When accessing /jp?callback=callme you should see:
callme({"hello": "World"});
If you omit the callback parameter an error will be returned as
it is required to know the callback name when using JSONP.
By default TurboGears will expect the callback name to be provided
in a callback parameter. This parameter has to be accepted by your
controller (otherwise you can use **kwargs like the previous examples).
If you need to use a different name for the callback parameter just provide
it in the render_params of your exposition:
@expose('jsonp', render_params={'callback_param': 'call'})
def jp(self, **kwargs):
return dict(hello='World')
Then instead of opening /jp?callback=callme to get the JSONP response
you will need to open /jp?call=callme as stated by the callback_param
option provided in the render_params.
If you want to expose a controller as both JSON and JSONP, just provide both expositions. You can then use TurboGears request extensions support to choose which response you need:
@expose('json')
@expose('jsonp')
def jp(self, **kwargs):
return dict(hello='World')
To get the JSON response simply open /jp.json while to get the
JSONP response go to /jp.js?callback=callme. If no extension is provided
the first exposition will be returned (in this case JSON).