A number of variables must be determined during each page hit but used in various places in my project, not just templates but also in views. So far, I've been using a context processor (called 'globals') to achieve this result. Please note that in the context processor I'm doing actual computations and database calls, so I don't just need a settings variable.
Since upgrading from Django 1.7 to 1.8, the variables returned by the context processor still show up in the templates, which is good, but they no longer show up in the views, at least nowhere I can find them.
In my contextprocessor, I have the following code:
def globals(request):
# NOTE: We DON'T simply need a variable from settings - in reality this is computed
if_this_is_true_then_we_alter_text = True
ctx = {
'var_from_contextprocessor': if_this_is_true_then_we_alter_text,
}
return ctx
Then, in my view, I have:
from django.template import RequestContext
from django.shortcuts import render
def show_globals(request):
ctx = RequestContext(request)
ctx['var_from_view'] = 'YES, we found it!' if ctx.has_key('var_from_contextprocessor') else 'NO, it ain\'t there!'
return render(request, 'show_globals.html', context_instance=ctx)
My template, show_globals.html
is as follows:
var_from_view: {{ var_from_view }}
var_from_contextprocessor: {{ var_from_contextprocessor }}
When running Django 1.7, the output in the template from the view will be "YES, we found it!". However, once I've upgraded to 1.8, the variable returned by the context processor appears to be available to the view, and so the text changed to "NO, it ain't there!". In both cases, var_from_contextprocessor
is however duly displayed in the template itself.
Is there still a way to retrieve variables from context processors in individual views? If not, any suggestions on how to achieve the same results without the use of a context processor?
Note that the basic problem I'm trying to solve is simply having variables calculated on the fly during each page hit, which are then available to both views and templates. I don't really care whether this is done by using a context processor or not.
Thanks in advance!
I've discovered a solution that helps, at least in my case.
So the desired result is having code, which is run at every page load, with results available both in all views and all templates.
A technical reasons for why this stopped working with a simple contextprocessor between Django versions 1.7 an 1.8 are quite well explained in a different answer by Daniel Roseman, so I won't go into them here.
Long story short, the trick is to use a middleware instead of a contextprocessor, but then have the contextprocessor inherit the variables from the middleware.
Considering the code in the original question, compare with the new code:
Contextprocessor:
def globals(request):
ctx = request.extravars # See example.middleware.ExtraVarsMiddleware
return ctx
Middleware:
class ExtraVarsMiddleware():
# For handing certain variables over to the context processor for global display.
def process_view(self, request, view_func, view_args, view_kwargs):
# NOTE: We DON'T simply need a variable from settings
if_this_is_true_then_we_alter_text = True
request.extravars = {
'var_from_contextprocessor': if_this_is_true_then_we_alter_text,
}
The "global" variables are hung on to the request object in an arbitrarily named dictionary called extravars. Note that while our "global" variable entry is still called var_from_contextprocessor
, it's a misnomer at this point since it's from the middleware, no longer the contextprocessor.
In order to activate this middleware, settings must be altered to include it, like so (defaults included):
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'example.middleware.ExtraVarsMiddleware',
)
The templates then have access to variables as previously, since the context processor has added the entirety of request.extravars
to its resulting context, which is then returned as per the code above.
Then, to access the variables in the view, we need a slight change from before:
from django.shortcuts import render
def show_globals(request):
ctx = request.extravars
ctx['var_from_view'] = 'YES, we found it!' if ctx.has_key('var_from_contextprocessor') else 'NO, it ain\'t there!'
return render(request, 'show_globals.html', context=ctx)
I've changed this code as little as possible to demonstrate the minimum changes needed. Only two things have changed, one is how the ctx
dictionary is acquired, this time without using RequestContext
. The second change is that we no longer pass context_instance
to the render
function, but simply context
.
For clarification, the corresponding file names in my example setup are:
example/contextprocessors.py
example/middleware.py
example/views.py
settings.py