Документация Django для состояния request_finished они предоставляют класс, а не экземпляр (не уверен, почему, было бы более полезно предоставить экземпляр). 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
Если у вас есть другая переменная с именем request, она сломается. Может быть адаптирован для проверки типа.