2010-03-03 33 views
5

如何使用request_finished信号获取并使用HttpRequest?如何从request_finished信号回调中访问请求?

有兴趣提取进行日志记录的URL。

当前的代码看起来是这样的:

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

我不确定你可以。你有没有想过看关键字参数字典?如果您只是希望在每次请求时记录一些信息,则应该使用中间件框架http://bit.ly/axVgOj进行研究。 – buckley 2010-03-03 05:08:59

+1

'sender'是模型类,不是实例。如果提供了实例数据,您可以在'** kwargs'中找到它,但是'request_finished'不包含任何实例数据。你也许可以使用'post_save'信号。如果你有一个耗时的任务,你不希望请求在完成之前等待,那么在新线程上运行它,比如'thread.start_new_thread(kwargs.get(“instance”)。do_something_time_consuming,())'。 – henrikstroem 2014-02-20 03:30:30

+0

django上的文档是一个类,而不是一个实例。 https://docs.djangoproject.com/en/1.9/ref/signals/#request-finished为什么接受完全错误的答案? – dalore 2016-05-03 15:20:12

回答

-1

尝试

sender.request_class.get_full_path() 

sender.request_class._get_request() 

或者,如果您想尝试使用middleware进行此操作,请按照山脉建议操作,这里是snippet that demos request timing using middleware

+3

困惑。这是否回答OP的问题?调用请求类中的任何未绑定方法只会导致抱怨它没有“自我”参数。 (因为,你知道,它是没有限制的。) – Chuck 2013-01-13 00:49:43

+0

@Chuck是的,我认为它的确如此。您可能必须调用'_get_request()'方法才能使用实例。 – 2014-02-19 05:43:34

+0

@Chuck是正确的。调用这个未绑定的方法显然不起作用 – jamylak 2015-01-10 09:55:52

2

为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 

如果您有任何其他变量称为请求,它会中断。可以适应检查类型。

相关问题