Search code examples
djangodjango-templatesthread-safetydjango-context

A thread-safe template context processor in Django?


What is the best practice for writing a thread-safe context processor in Django?

Say, I want to pass some variables to templates, which are set in the corresponding views, and could be different for different view-template pairs.

One solution would be to manually pass each variable in the context:

return render_to_response('template.html', {'var1':var1,... 'var10':var10},
                           context_instance=RequestContext(request))

To keep it DRY, however, I would rather use a context processor. But I worry about thread safety as it seems to require a global store. Here is my solution using a context processor, which ties each variable to the request. Thanks for your comments and suggestions.

In context_processor.py:

store = {}
def add_context(request, key, value):
    if request not in store:
        store[request] = {}
    store[request][key] = value
    return
def misc_context_processor(request):
    return store.pop(request,{})

In views.py:

import context_processor
def view(request):
    ...
    if x == y:
        context_processor.add_context(request,'var1','value1')
    else:
        context_processor.add_context(request,'var2','value2')
    ...
    return render_to_response('template.html', {},
                              context_instance=RequestContext(request))

In settings.py:

TEMPLATE_CONTEXT_PROCESSORS = (
    'django.core.context_processors.request',
    'django.core.context_processors.debug',
    'django.core.context_processors.i18n',
    ...,
    'appname.context_processor.misc_context_processor',
)

Solution

  • context_processors are for context variables you want set up in every view to be available for every template. If you have view specific context, that rightfully belongs in the view. If you're trying to push the construction of view specific context dictionaries off to a context_processor then your really creating an unnecessary headache and a landmine if anyone else ever has to touch your code. Use the tools for what they are meant to be used for.

    Additionally it's a lot easier to write and read:

    context = {
        'var1': value1,
        'var2': value2,
    }
    

    than it is to try to figure out what this is doing:

    context_processor.add_context(request, 'var1', value1)
    context_processor.add_context(request, 'var2', value2)
    

    Or maybe what your want is like this:

    def view(request):
        context = {}
        ...
        if x == y:
            context['var1'] = value1
        else:
            context['var2'] = value2
        ...
        return render_to_response('template.html', context,
                context_instance=RequestContext(request))
    

    Or maybe even use context.update({ 'var1': value1 })

    I'm missing how the second one is more DRY. Considering you're going to have to do that in every view that needs those variables anyway...

    If you have repeatable context generation use class based views to abstract that out in a reasonable fashion. If you really just have 10variables and each template only needs some of them (but they vary from template to template) then just simply make all of them available to all templates. As long as the generation isn't expensive this works great, and keep in mind that querysets are lazy so if you never evaluate them they never hit the db