Is there a better way to switch between HTML and JSON output in Pyramid?
# /test{.format} no longer seems to work...
config.add_route('test', '/test.{ext}', view='ms.views.test')
views.py:
from pyramid.response import Response
from pyramid.renderers import render
import json
def test(request):
extension = request.matchdict['ext']
variables = {'name' : 'blah', 'asd' : 'sdf'}
if extension == 'html':
output = render('mypackage:templates/blah.pt', variables, request=reques开发者_JAVA百科t)
if extension == 'json':
output = json.dumps(variables)
return Response(output)
Is there an easier way to do this? With Pylons, it was a simple:
def test(self, format='html'):
c.variables = {'a' : '1', 'b' : '2'}
if format == 'json':
return json.dumps(c.variables)
return render('/templates/blah.html')
I suspect I'm approaching this the wrong way...?
I think, the better way is to add the same view twice with difference renderers. Suppose we have the following view:
def my_view(request):
return {"message": "Hello, world!"}
Now in our configuration we can add the same view twice:
from pyramid.config import Configurator
config = Configurator()
config.add_route('test', '/test', my_view, renderer="templates/my_template.mako")
config.add_route('test', '/test', my_view, renderer="json", xhr=True)
What we have now:
- View
my_view
will render template"templates/my_template.mako"
with returned dict provided as context if we will point our browser to url/test
. - If we will make XHR request with
my_view
will be called again, but now returned dict will be encoded as JSON and transmitted back to caller (please read docs about checking if request was done via XHR).
The same idea we can use for defining different routes but with the same view attached to them:
from pyramid.config import Configurator
config = Configurator()
config.add_route('test', '/test', my_view, renderer="templates/my_template.mako")
config.add_route('test_json', '/test.json', my_view, renderer="json")
Now /test
will trigger template rendering, but /test.json
will return just JSON encoded string.
You can go further and make dispatching to the right renderer via accept
argument of add_router
method:
from pyramid.config import Configurator
config = Configurator()
config.add_route('test', '/test', my_view, renderer="templates/my_template.mako")
config.add_route('test', '/test', my_view, renderer="json", accept="application/json")
If request comes with header Accept
set to application/json
value JSON will be returned, otherwise you got rendered template.
Note, this will work only if you have predefined set of data formats in which you want to encode responses from your views, but it's the usual case. In case you need dynamical dispatching you can decorate your views with decorate
argument of add_route
which will choose the right renderer with your rules.
Is this what you're looking for? Pylons and Pyramid have different API's. So they will be different. You can make them a little more similar, but you can't make them identical.
def test(request):
extension = request.matchdict['ext']
variables = {'name' : 'blah', 'asd' : 'sdf'}
if extension == 'json':
return Response( json.dumps(variables) )
return Response( render('mypackage:templates/blah.pt', variables, request=request) )
Pyramid's URL Dispatch is very powerful and flexible mechanism. First of all, we'll write correct url pattern. In route pattern syntax we can use regular expressions for replacement markers.
'/test{ext:\\..*}'
Here we can see that url path should contain . (period) and then any symbols. All symbols including . (period) will be under the key ext
in request.matchdict
.
Of course, we can complicate the regular expression in order to specify what extensions there may be:
'/test{ext:\\.(html|json)}'
Then we adding route with our pattern:
config.add_route('test',
pattern='/test{ext:\\.(html|json)}')
Want to add, that we can specify the set of extensions using custom predicates.
In order to specify the default extension we can use simple pregenerator.
def default_extension(ext):
def pregenerator(request, elements, kw):
if 'ext' not in kw:
kw['ext'] = ext
return elements, kw
return pregenerator
config.add_route('test',
pattern='/test{ext:\\.(html|json)}',
pregenerator=default_extension('.html'))
request.route_path('test')
# '/test.html'
request.route_path('test', ext='.json')
# '/test.json'
After that we'll Traversal to help us switch between html and json output:
config.add_route('test',
'/test{ext:\\.(html|json)}',
pregenerator=default_extension('.html'),
traverse='{ext}')
With the traverse
argument in add_route
we force our application to be hybrid. And we should understand that the factory which will provide context for our views must not contain the keys matching our extensions. The default root factory doesn't.
views.py:
from pyramid.view import view_config, view_defaults
@view_defaults(route_name='test')
class Test(object):
def __init__(self, request):
self.request = request
self.variables = {
'name': 'blah',
'asd': 'sdf'
}
@view_config(name='.html', renderer='mypackage:templates/blah.pt')
def html(request):
return {
'request': request,
'variables': self.variables
}
@view_config(name='.json', renderer='json')
def json(request):
return {
'request': request,
'variables': self.variables
}
Here we've created class Test
and specify route name for it. And then we've separated methods by names of our extensions.
Try this way:
def test(self, format='html'):
c.variables = {'a' : '1', 'b' : '2'}
if format == 'json':
return Response(json = c.variables)
return render_to_response('/templates/blah.html')
This is the most similar to your pylons example. Also it shows some more friendly way to render a template or some JSON to a response.
精彩评论