41đź‘Ť
The method I am going for at the moment uses a subclass of HttpResponse:
from django.template import loader
from django.http import HttpResponse
# use custom response class to override HttpResponse.close()
class LogSuccessResponse(HttpResponse):
def close(self):
super(LogSuccessResponse, self).close()
# do whatever you want, this is the last codepoint in request handling
if self.status_code == 200:
print('HttpResponse successful: %s' % self.status_code)
# this would be the view definition
def logging_view(request):
response = LogSuccessResponse('Hello World', mimetype='text/plain')
return response
By reading the Django code I am very much convinced that HttpResponse.close() is the latest point to inject code into the request handling. I am not sure if there really are error cases that are handled better by this method compared to the ones mentioned above, so I am leaving the question open for now.
The reasons I prefer this approach to the others mentioned in lazerscience’s answer are that it can be set up in the view alone and does not require middleware to be installed. Using the request_finished signal, on the other hand, wouldn’t allow me to access the response object.
33đź‘Ť
If you need to do this a lot, a useful trick is to have a special response class like:
class ResponseThen(Response):
def __init__(self, data, then_callback, **kwargs):
super().__init__(data, **kwargs)
self.then_callback = then_callback
def close(self):
super().close()
self.then_callback()
def some_view(request):
# ...code to run before response is returned to client
def do_after():
# ...code to run *after* response is returned to client
return ResponseThen(some_data, do_after, status=status.HTTP_200_OK)
…helps if you want a quick/hacky “fire and forget” solution without bothering to integrate a proper task queue or split off a separate microservice from your app.
- [Django]-Handle `post_save` signal in celery
- [Django]-Difference between 'related_name' and 'related_query_name' attributes in Django?
- [Django]-Django set DateTimeField to database server's current time
3đź‘Ť
I suppose when talking about middleware you are thinking about the middleware’s process_request
method, but there’s also a process_response
method that is called when the HttpResponse
object is returned. I guess that will be the latest moment where you can find a hook that you can use.
Furthermore there’s also a request_finished
signal being fired.
- [Django]-No module named django but it is installed
- [Django]-Django FileField upload is not working for me
- [Django]-Django Admin: OneToOne Relation as an Inline?
2đź‘Ť
I found a filthy trick to do this by accessing a protected member in HttpResponse.
def some_view(request):
# ...code to run before response is returned to client
def do_after():
# ...code to run *after* response is returned to client
response = HttpResponse()
response._resource_closers.append(do_after)
return response
It works in Django 3.0.6 , check the "close" function in the prototype of HttpResponse.
def close(self):
for closer in self._resource_closers:
try:
closer()
except Exception:
pass
# Free resources that were still referenced.
self._resource_closers.clear()
self.closed = True
signals.request_finished.send(sender=self._handler_class)
- [Django]-What is the way to ignore/skip some issues from python bandit security issues report?
- [Django]-Django logging of custom management commands
- [Django]-How to view database and schema of django sqlite3 db
1đź‘Ť
I modified Florian Ledermann’s idea a little bit… So someone can just use the httpresponse function normally, but allows for them to define a function and bind it to that specific httpresponse.
old_response_close = HttpResponse.close
HttpResponse.func = None
def new_response_close(self):
old_response_close(self)
if self.func is not None:
self.func()
HttpResponse.close = new_response_close
It can be used via:
def myview():
def myfunc():
print("stuff to do")
resp = HttpResponse(status=200)
resp.func = myfunc
return resp
I was looking for a way to send a response, then execute some time consuming code after… but if I can get a background (most likely a celery) task to run, then it will have rendered this useless to me. I will just kick off the background task before the return statement. It should be asynchronous, so the response will be returned before the code is finished executing.
—EDIT—
I finally got celery to work with aws sqs. I basically posted a "how to". Check out my answer on this post:
Cannot start Celery Worker (Kombu.asynchronous.timer)
- [Django]-How to get value from form field in django framework?
- [Django]-Django template tag to truncate text
- [Django]-How do I set up Jupyter/IPython Notebook for Django?