如何使用request_finished信号获取和使用HttpRequest?
有兴趣提取用于记录目的的网址。
当前代码如下所示:
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)
生成此
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']
答案 0 :(得分:3)
对于request_finished状态的Django文档,它们提供的类不是实例(不确定原因,提供实例会更有用)。 https://docs.djangoproject.com/en/1.9/ref/signals/#request-finished
所以信号让你知道一个请求已经完成,但不是哪个请求或任何细节。您有2个选项来获取请求。已经提到的一个是将请求存储在中间件中的线程本地存储中。
以下是存储请求的示例。但是你可以使用它来存储最后会被调用的函数。
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
当您需要访问当前请求时,可以导入函数get_current_request
并在任何其他方法中使用。
函数request_queue
允许您排队要执行的函数和参数。一个功能是你可以多次排队一个昂贵的功能,它只会被执行一次。
因此,在request_finished
处理程序中,您可以调用get_current_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
如果您有任何其他名为request的变量,它将会中断。也可以适应检查类型。
答案 1 :(得分:1)
我想我找到了最简单的解决方案。
在the official Django rep中,我发现requests_finished
here的唯一用法。
现在,我可以在manage.py
的入口点中轻松覆盖此方法:
from django.http.response import HttpResponseBase
class OverriddenHttpResponseBase:
@staticmethod
def close(self):
for closable in self._closable_objects:
try:
closable.close()
except Exception:
pass
self.closed = True
# here you can access your request using self._closable_objects
# you can either send it to request_finished
signals.request_finished.send(sender=<whatever data you want>)
# or code your stuff here without using request_finished at all
if __name__ == '__main__':
HttpResponseBase.close = OverriddenHttpResponseBase.close
答案 2 :(得分:-2)
尝试
sender.request_class.get_full_path()
或
sender.request_class._get_request()
或者,如果您想尝试使用middleware执行此操作,正如mountainswhim建议的那样,这里是snippet that demos request timing using middleware。