How do I obtain and use the HttpRequest using the request_finished signal?
Interested in extracting the url for logging purposes.
Current code looks something like this:
import logging
def write_to_file(sender, **kwargs):
logging.debug(type(sender))
logging.debug(dir(sender))
from django.core.signals import request_finished
request_finished.connect(write_to_file)
generates this
2010-03-03 13:18:44,602 DEBUG <type 'type'>
2010-03-03 13:18:44,602 DEBUG ['__call__', '__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__weakref__', '_get_traceback', 'apply_response_fixes', 'get_response', 'handle_uncaught_exception', 'initLock', 'load_middleware', 'request_class', 'response_fixes']
Django documentation for request_finished state they provide the class not the instance (not sure why, it would have been more useful to provide the instance). https://docs.djangoproject.com/en/1.9/ref/signals/#request-finished
So the signal just let's you know a request has finished, but not which request or any detail of it. You have 2 options to get the request. One, which has been mentioned, is to store the request into thread local storage in a middleware.
Here is an example which stores the request. But you can use it store up functions that will be called at the end.
import collections
import threading
import structlog
from django.utils.cache import patch_vary_headers
logger = structlog.get_logger()
thread_locals = threading.local()
def get_current_request():
"""
This will return the current request object
but if the response has been returned the request
object will be cleaned up
"""
return getattr(thread_locals, 'request', None)
def request_queue(func, func_id=None, *args, **kwargs):
"""
Helper function to queue up a function
and arguments to be run at the end of the request
if no request, will run straight away
Usage:
request_queue(function_to_run, args=(args1, args2), kwargs={'key':'value'})
"""
request = get_current_request()
if not request:
# run the func
func(*args, **kwargs)
return
# else
# use the supplied id if given
if not func_id:
# otherwise use the memory address
func_id = id(func)
# store the func and arguments as a tuple under the func id
request.queue[func_id] = (func, args, kwargs)
class RequestQueueMiddleware(object):
"""
Use this middleware to get access to the request object
and to use it to queue functions to run
"""
def process_request(self, request):
thread_locals.request = request
# each request gets a new queue
request.queue = collections.OrderedDict()
def process_exception(self, request, exception):
self.process_queue(request)
self.cleanup()
def process_response(self, request, response):
self.process_queue(request)
self.cleanup()
return response
def cleanup(self):
try:
del thread_locals.request
except AttributeError:
pass
def process_queue(self, request):
if not request:
request = get_current_request()
if request and hasattr(request, 'queue'):
for (func, args, kwargs) in getattr(request, 'queue', {}).itervalues():
func(*args, **kwargs)
del request.queue
The function get_current_request
can be imported and used in any other method when you need access to the current request.
The function request_queue
allows you to queue up a function and arguments to be executed. A feature is that you could queue up an expensive function many times and it will only be executed once.
So in your request_finished
handler you can call get_current_request
to get the current request. But in the above implementation you will need to remove the cleanup code. I don't know if keeping the request object on thread local storage will leak.
The other option which doesn't require any middleware is to inspect the stack frames until you find the request.
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
If you have any other variable called request it will break. Could be adapted to check type as well.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With