Timmy
Timmy

Reputation: 12828

A better pattern for ajax loading with pyramid?

I've read up on using different renderers or overriding renderer but I'm wondering if there's a better way to handle this pattern.

Right now, in my view I'm returning a set of items to the template:

@view_config(
    route_name = 'name',
    permission = 'perm',
    renderer   = 'r.mako'
)
def r( request ):
     items = get_items()
     return { 'items': items }

Now I want the ajax version to just renderer a subset of it, also with some data. My current working code:

@view_config(
    route_name = 'name',
    permission = 'perm',
    renderer   = 'r.mako'
)
def r( request ):
     items = get_items()

     if ajax:
         return Response( to_json( {
                             'data1':  1,
                             'data2':  2,
                             'data3':  3,
                             'html':  renderers.render( 'shortr.mako',
                                                        { 'items': items },
                                                        request )
                         } )

     return { 'items': items }

I guess specifically I wonder if there's a cleaner way to override the renderer and then wrap it in something, without explicitly calling render and making sure I got the dict right or request as a param. thanks

Upvotes: 4

Views: 2642

Answers (2)

Michael Merickel
Michael Merickel

Reputation: 23331

I would suggest using 2 views which properly allow you to apply a different "look-and-feel" (responses) to the same data.

def get_items(request):
    return {} # values that you can pick and choose from in each view

@view_config(route_name='name', permission='perm', xhr=True, renderer='json')
def r_ajax(request):
    items = get_items(request)
    return {
        'data1': 1, 'data2': 2, 'data3': 3,
        'html': renderers.render('shortr.mako', {'items': items}, request),
    }

@view_config(route_name='name', permission='perm', renderer='r.mako')
def r_html(request):
    items = get_items(request)
    return items

If you're afraid of repeating things for the view configuration, Pyramid 1.3 comes with a cool new feature on its class-based views:

@view_defaults(route_name='name', permission='perm')
class R(object):
    def __init__(self, request):
        self.request = request
        self.items = # ...

    @view_config(xhr=True, renderer='json')
    def ajax(request):
        return {
            'data1': 1, 'data2': 2, 'data3': 3,
            'html': renderers.render('shortr.mako', {'items': items}, request),
        }

    @view_config(renderer='r.mako')
    def html(request):
        return self.items

Upvotes: 8

dskinner
dskinner

Reputation: 10857

I'm not familiar with pyramid or what 'r.mako' is referencing, but you could probably hook in to the request before the controller is called with a custom function that inspects the Accepts headers of the request, looking for 'text/javascript' or 'application/json' as the foremost Accepts, and then set a flag on the request object (or have that method factored out for use in your r function).

Then do a custom renderer to handle either parsing with mako or dumping a json string

# pseudo-code
def before_controller(request, response):
    if 'text/html' in request.headers.accepts:
        request.is_json = False
    elif 'application/json' in request.headers.accepts:
        response.headers.set('Content-type', 'application/json')
        request.is_json = True

# pseudo-code
def my_renderer(request, response, result):
    if 'text/html' in request.headers.accepts:
        return # render html
    elif 'application/json' in request.headers.accepts:
        response.headers.set('Content-type', 'application/json')
        return json.dumps(result)

#
def r(request):
    items = get_items()
    if request.json:
        pass # update items with additional data
    return {'items': items} # one point of return

The method would also mean no extra leg-work if you dont need to do any additional processing on items, you simply return the result as normal and receive the json object on the other end.

If you cant hook into pyramid before the controller is called, you could write a convenience function to call is_json(request) and use that in the controller and the renderer for determining output and setting content-type header

Upvotes: 1

Related Questions