Saturday 28 August 2010 — This is 14 years old. Be careful.
As my Django sites get larger and larger, there inevitably comes a point where I want access to the current request from deep inside some function that doesn’t have the request object. The latest reason was that I wanted a model class helper to have access to the session so it could access some debug flags.
The first option for making this work was to pass the request object through two or three layers of code that otherwise didn’t need a request. This meant changing the signature and callers of the two or three functions, which felt messy. The prospect of it made me unhappy.
The second option was to create a way for any code invoked as part of a request to get access to the request, even if it weren’t passed to it explicitly. I think of this as a global request object.
Of course, there isn’t really a single global request object, since there can be many threads, each of which is handling a separate request. We need a way to associate the request with a thread, and then to get the request for our thread. This middleware does the job nicely:
from django.utils.thread_support import currentThread
_requests = {}
def get_request():
return _requests[currentThread()]
class GlobalRequestMiddleware(object):
def process_request(self, request):
_requests[currentThread()] = request
I didn’t write this code, I got it from here and here, not sure who wrote it first. I’m also not sure why Django provides its own currentThread function when the Python standard module threading provides thread locals to achieve the same effect.
There’s another way to get a global request object, but you probably won’t like it:
def get_request():
"""Walk up the stack, return the nearest first argument named "request"."""
frame = None
try:
for f in inspect.stack()[1:]:
frame = f[0]
code = frame.f_code
if code.co_varnames and code.co_varnames[0] == "request":
return frame.f_locals['request']
finally:
del frame
This function looks at the stack frames of all of its callers, looking for one with a first argument named “request”. If found, it returns the value. The problem with this function is that it can be fooled, and will return the “request” it finds regardless of its type.
In its defense: GlobalRequestMiddleware requires more machinery, and adds a tiny tax to every request. If your need for the global request object is rare, the frame-based get_request() may be better for you. Also, it’s a (nasty) technique that can be adapted to other situations.
Comments
The caveat about the 'certain circumstances' is because you might be looking for something on the request when you're not sure whether it will be there at all, and so testing some random stack variable for that thing will simply give you a false negative. In that case, just check the stack variables for something requests always have but hardly anything else ever would, such as META.
-Eddie
I think you should implement process_response() in the nmiddleware to empty delete the key from the dictionary.
This is not required if you use the threading.local as Noah said.
Attach a curried signal handler with the request as a parameter in process_request, detach it from process_response.
Add a comment: