如何从request_finished信号回调访问请求? [英] How do I access the request from the request_finished signal callback?

查看:371
本文介绍了如何从request_finished信号回调访问请求?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何使用request_finished信号获取和使用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__ ',_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _,_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, 'load_middleware','request_class','response_fixes']


解决方案

Django文档为request_finished状态,他们提供的类不是实例(不知道为什么,这将是更有用的提供实例)。 https://docs.djangoproject.com/en/1.9/ref/信号/#请求完成



所以信号只是让你知道一个请求已经完成,但没有哪个请求或任何细节。您有两个选项来获取请求。已经提到的一个是将请求存储在中间件中的线程本地存储中。



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

 导入集合
导入线程

import struct log
from django.utils.cache import patch_vary_headers

logger = structlog.get_logger()

thread_locals = threading.local()


def get_current_request():

这将返回当前请求对象
,但如果响应已返回请求
对象将被清理

return getattr(thread_locals,'request',None)


def request_queue(func,func_id = * args,** kwargs):

帮助函数排队函数
和参数在请求结束时运行
如果没有请求,将直接运行
用法:
request_queue(function_to_run,args =(args1,args2),kwargs = {'key':'value'})

request = get_current_request()
如果没有请求:
#运行func
func(* args,** kwargs)
return
#else
#使用提供的id如果给定
如果没有func_id:
#否则使用内存地址
func_id = id(func)
#将func和参数作为一个元组存储在func id下
request.queue [func_id ] =(func,args,kwargs)


class RequestQueueMiddleware(object):

使用这个中间件来访问请求对象
并使用它来排队函数运行


def process_request(self,request):
thread_locals.request = request
#each requests获得一个新队列
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(请求)
self.cleanup()
返回响应

def cleanup(self):
try:
del thread_locals.request
except AttributeError:
pass

def process_queue(self,request):
如果没有请求:
request = get_current_request()
如果请求和hasattr(请求,'queue'):getattr中的(func,args,kwargs)的
(request,'queue',{})itervalues():
func(* args,** kwargs)
del request.queue

函数 get_current_request

函数 request_queue 可以导入并使用任何其他方法,当您需要访问当前请求。允许您对要执行的函数和参数进行排队。一个功能是你可以排队一个昂贵的功能很多次,它只会执行一次。



所以在你的 request_finished 处理程序可以调用 get_current_request 来获取当前请求。但是在上述实现中,您将需要删除清理代码。我不知道如果保持请求对象在线程本地存储将泄漏。



另一个不需要任何中间件的选项是检查堆栈帧,直到你找到请求。

  def get_request():
走起堆栈,返回最近的第一个参数命名为request。
frame = None
try:
for f in inspect.stack()[1:]:
frame = f [0]
code = frame.f_code
如果code.co_varnames和code.co_varnames [0] ==request:
return frame.f_locals ['request']
finally:
del框架

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


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.

这篇关于如何从request_finished信号回调访问请求?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆