Question

My application is running on App Engine and is implemented using Werkzeug and Jinja2. I'd like to have something functionally equivalent of Django's own context processor: a callable that takes a request and adds something to the template context. I already have a "context processors" that add something to the template context, but how do I get this request part working? I implemented context processors as a callables that just return a dictionary that later is used to update context.

For example, I'd like to add something that is contained in request.environ.

Was it helpful?

Solution

One way of achieving this is through late-bound template globals using the thread-local proxy in Werkzeug.

A simple example that puts the request into the the template globals:

from werkzeug import Local, LocalManager
local = Local()
local_manager = LocalManager([local])

from jinja2 import Environment, FileSystemLoader

# Create a global dict using the local's proxy to the request attribute
global_dict = {'request': local('request')}
jinja2_env = Environment(loader=FileSystemLoader('/'))
jinja2_env.globals.update(global_dict)

def application(environ, start_response):
    """A WSGI Application"""
    # later, bind the actual attribute to the local object
    local.request = request = Request(environ)

    # continue to view handling code
    # ...

application = local_manager.make_middleware(application)

Now in any of your templates, the current request will appear bound to the variable "request". Of course that could be anything else in environ. The trick is to use the local proxy, then set the value before you render any template.

I should probably also add that a framework like Glashammer (Werkzeug+Jinja2) streamlines this process for you by using events. Many functions can connect to the events during the process of the WSGI call (for example, when a request is created) and they can put stuff in the template namespace at that point.

OTHER TIPS

Well, using what Ali wrote I came to the solution that is specific to App Engine (because of its import cache). Unfortunately, Ali's code does not work with App Engine, because the code that sets Jinja globals are imported only once (making the globals effectively static).

I had to write my own render() function and update the context there. For completeness sake, below is the code I came to:

def render(template, **kwargs):
    response_code = kwargs.pop('response_code', 200)
    mimetype = kwargs.pop('mimetype', 'text/html')
    for item in getattr(settings, 'CONTEXT_PROCESSORS', []):
        try:
            processor = import_string(item)
            kwargs.update(processor(local.request))
        except (ImportError, AttributeError), e:
            logging.error(e)
    return Response(jinja_env.get_template(template).render(**kwargs),
        status=response_code, mimetype=mimetype)

This is App Engine specific. In other environments Ali's code works as expected (and that's why I am retagging my question).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top