Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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=request)

    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...?

like image 335
dave Avatar asked Jan 08 '11 09:01

dave


3 Answers

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:

  1. 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.
  2. 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.

like image 159
andreypopp Avatar answered Nov 04 '22 03:11

andreypopp


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) )
like image 43
S.Lott Avatar answered Nov 04 '22 04:11

S.Lott


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.

like image 30
scraplesh Avatar answered Nov 04 '22 03:11

scraplesh