2010-03-03 36 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 

如果您有任何其他變量稱爲請求,它會中斷。可以適應檢查類型。

相關問題