views:

1148

answers:

3

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.

+3  A: 

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.

Ali A
The Environment constructor does not take "globals" argument - this is an attribute of Environment instance (you have to modify it just after creating env).Other than that, this is perfectly what I was looking for. Thank you.
zgoda
Ah right, thanks, I'll update the example. I did from memory.
Ali A
Unfortunately, this can not be done this way with AppEngine due to imports cache (imported modules are cached between requests) - the globals dict (and request object) would be cached until module reload.
zgoda
+3  A: 

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

zgoda
A: 

You can from django.template import RequestContext, and populate your context with dictionaries inside it.

M. Utku ALTINKAYA
Question was about Werkzeug and Jinja2. What Django's RequestContext has to do with it?
temoto