2010-03-03 11 views
5

¿Cómo obtengo y uso el HttpRequest usando la señal request_finished?¿Cómo accedo a la solicitud desde la devolución de llamada de señal request_finished?

Interesado en extraer la URL con fines de registro.

código actual es como la siguiente:

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) 

genera este

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

No estoy seguro de que pueda hacerlo. ¿Estás tratando de mirar la palabra clave arguments dict? Si solo desea registrar alguna información en cada solicitud, debe considerar usar el marco de Middleware http://bit.ly/axVgOj. – buckley

+1

'sender' es la clase de modelo, no una instancia. Si se proporcionan datos de instancia, lo encontrará en '** kwargs', sin embargo' request_finished' no incluye ningún dato de instancia. Es posible que pueda usar la señal 'post_save' en su lugar. Si tiene una tarea que consume mucho tiempo y no desea que la solicitud espere antes de finalizar, ejecútela en un nuevo hilo, diga 'thread.start_new_thread (kwargs.get (" instance "). Do_something_time_consuming,())'. – henrikstroem

+0

Los documentos en django dicen que es una clase y no una instancia. https://docs.djangoproject.com/en/1.9/ref/signals/#request-finished ¿Por qué se ha aceptado una respuesta completamente incorrecta? – dalore

Respuesta

-1

Trate

sender.request_class.get_full_path() 

o

sender.request_class._get_request() 

O, si quiere intentar hacer esto con middleware, como sugirió mountainswhim, aquí hay un snippet that demos request timing using middleware.

+3

Confundido. ¿Respondió esto a la pregunta del OP? Llamar a cualquier método independiente en la clase de solicitud solo provoca quejarse de que no tiene un argumento "propio". (Dado que, ya sabes, está desatado.) – Chuck

+0

@Chuck, sí, creo que sí. Es posible que deba invocar el método '_get_request()' para tener en sus manos la instancia. –

+0

@Chuck es correcto. Llamar a este método independiente obviamente no funciona. – jamylak

2

La documentación de Django para el estado request_finished proporciona la clase, no la instancia (no estoy seguro de por qué, hubiera sido más útil proporcionar la instancia). https://docs.djangoproject.com/en/1.9/ref/signals/#request-finished

Por lo tanto, la señal solo le informa que la solicitud ha finalizado, pero no la solicitud ni ningún detalle de la misma. Tienes 2 opciones para obtener la solicitud. Uno, que se ha mencionado, es almacenar la solicitud en el almacenamiento local de subprocesos en un middleware.

Aquí hay un ejemplo que almacena la solicitud. Pero puede usarlo para almacenar funciones que serán llamadas al final.

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 

La función get_current_request pueden ser importados y utilizados en cualquier otro método cuando necesite acceder a la solicitud actual.

La función request_queue le permite poner en cola una función y los argumentos que se ejecutarán. Una característica es que puede poner en cola una función costosa muchas veces y solo se ejecutará una vez.

Por lo tanto, en su controlador request_finished puede llamar al get_current_request para obtener la solicitud actual. Pero en la implementación anterior, deberá eliminar el código de limpieza. No sé si se perderá el mantenimiento del objeto de solicitud en el almacenamiento local de subprocesos.

La otra opción que no requiere ningún middleware es inspeccionar los marcos de la pila hasta que encuentre la solicitud.

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 

Si tiene cualquier otra variable llamados solicitud se romperá. Podría ser adaptado para verificar el tipo también.

Cuestiones relacionadas