Django 如何从request\u finished signal回调访问请求?

Django 如何从request\u finished signal回调访问请求?,django,Django,如何使用请求完成信号获取和使用HttpRequest? 有兴趣提取用于日志记录目的的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(wri

如何使用请求完成信号获取和使用HttpRequest?

有兴趣提取用于日志记录目的的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']
2010-03-03 13:18:44602调试
2010-03-03 13:18:44602调试“如果你的需求是,未来的需求是,未来的需求是,未来的需求是,未来的需求是,未来的需求是,未来的需求是,未来的需求是,UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU“,”weakref“,”获取回溯“,”应用响应修复“,”获取响应“,”处理未捕获异常“,”初始化锁“,”加载中间件“,”请求类“,“响应修复”]
试试看


或者,如果您想尝试这样做,正如MountainsWhen所建议的,这里有一个.Django文档,用于请求完成状态,它们提供的是类而不是实例(不确定为什么,提供实例会更有用)

因此,信号只是让您知道一个请求已经完成,而不是哪个请求或它的任何细节。您有两个选项来获取请求。前面提到的一个选项是将请求存储到中间件中的线程本地存储中

下面是一个存储请求的示例,但您可以使用它存储将在最后调用的函数

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\u current\u request
,并在任何其他方法中使用

函数
request\u queue
允许您对要执行的函数和参数进行排队。一个功能是,您可以对昂贵的函数进行多次排队,并且只执行一次

因此,在您的
request\u finished
处理程序中,您可以调用
get\u current\u 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的变量,它将中断。也可以调整为检查类型。

我想我已经找到了最简单的解决方案

仔细查看,我发现了
请求的唯一用法\u finished

现在,我可以在
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
从django.http.response导入HttpResponseBase
类重写HttpResponseBase:
@静力学方法
def关闭(自我):
对于在self.\u closable\u对象中可关闭:
尝试:
closable.close()
除例外情况外:
通过
self.closed=True
#在这里,您可以使用self.\u closable\u对象访问您的请求
#您可以将其发送到请求\完成
signals.request\u finished.send(发送方=)
#或者在这里编写代码,而不使用request\u finished
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
HttpResponseBase.close=覆盖HttpResponseBase.close

我不确定您是否可以。您是否尝试查看关键字参数dict?如果您只是想在每次请求时记录一些信息,您应该考虑使用中间件框架。
sender
是模型类,而不是实例。如果提供了实例数据,您将在
**kwargs
中找到它,但是
request_finished
不包含任何实例数据。您可以使用
post_save
信号。如果您有一个耗时的任务,您不希望请求在完成之前等待,请在新线程上运行它,比如
thread。启动新线程(kwargs.get(“实例”)。做一些耗时的事情,())
。django上的文档说明它是一个类而不是一个实例。为什么接受了完全错误的答案?困惑。这回答了OP的问题吗?对请求类调用任何未绑定的方法只会导致抱怨它没有“self”参数。(因为,你知道,它是未绑定的。)@Chuck是的,我想是的。您可能必须调用
\u get\u request()
方法才能获得实例。@Chuck是正确的。调用此未绑定方法显然不起作用。我会遇到以下错误
***TypeError:必须使用WSGIRequest实例作为第一个参数来调用未绑定方法\u get\u post()(取而代之的是一无所获)
dir(sender.request\u class.\u get\u post())知道为什么吗?为什么这是一个明显错误的公认答案?!sender是一个类,对我来说是WSGIHandler。你可以看到django用:
信号调用它。request\u finished.send(sender=self.\u handler\u class)
。WSGIHandler类没有类级别的方法。
request\u class
属性指向另一个名为
WSGIRequest
的类。但是如上所述,这是一个类,因此您不能调用需要像您建议的那样自我的方法。因此这完全是错误的。听起来不错。改进可能是从HttpRespons继承eBase并实现一个关闭例程,调用“super”,然后执行您需要执行的任何操作。
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
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