How to get response content from a Response() object in a view decorator?

Let's say I have a decorator that can be applied to a DRF class based View. A basic version of the decorator i have is as follows:-

`class LogRequest:
    def __init__(self, log_success=True, methods_to_log=None):
        self.log_success = log_success
        self.methods_to_log = methods_to_log if methods_to_log is not None else ["post"]

    def __call__(self, view_class):
        view_class.methods_to_log = self.methods_to_log
        view_class.log_success = self.log_success
        for method_name in self.methods_to_log:
            if hasattr(view_class, method_name):
                method = getattr(view_class, method_name)
                decorated_method = self.wrap_method(method)
                setattr(view_class, method_name, decorated_method)
        return view_class

    def wrap_method(self, method):
        @wraps(method)
        def wrapped_method(self, request, *args, **kwargs):
    
            method_name = request.method.lower()
            if method_name in getattr(self, "methods_to_log", []):
                log = Log(path=request.path, method=method_name)
                log.request_body = Log.parse_request_body(request)
              
                try:
                    response = method(self, request, *args, **kwargs)
                except Exception as e:
                    tb = traceback.format_exc()
                    log.error_traceback = tb
                    log.status_code = 500
                    log.response_body = str(e)
                    log.save()
                    return JsonResponse({"error": str(e)}, status=500)

                if self.log_success or response.status_code >= 400:
                    log.status_code = response.status_code
                    if 'application/json' in response.get('Content-Type', ''):
                        response_content = response.rendered_content.decode("utf-8")
                        log.response_body = response_content
                    log.save()

                return response

            return method(self, request, *args, **kwargs)

        return wrapped_method`

The decorator is applied on view class as following:-

`@LogRequest(log_success = True,methods_to_log=["post","get"])
class TestDecoView(generics.GenericAPIView):
    permission_classes = (AllowAny,)

    def post(self,request,*args, **kwargs):
        return JsonResponse({"message":"OK"},status=403)
    def get(self,request,*args, **kwargs):
        raise Exception("HAHA")`

The problem is , when log_success is true and i want to log succesful responses or 400 errors, i am getting errors like JsonResponse' object has no attribute 'rendered_content' or 'Response' object has no attribute 'rendered_content' depending on what kind of response i am sending.

The Exceptions are being logged perfectly, rest is not. Any idea how to fix it?

I tried setting the accepted_renderer manually and that's not giving an error. But it's saving the response content as a blank string. Any idea how to fix it?

You are facing this issue mainly because JsonResponse and Django REST Framworks response objects, acts completely differently in terms of rendering the response content.

To be specific to DRF's Response object rendered_content is a good option, it only gets populated after the response is rendered. For JsonResponse it does not have this attribute leading to errors.

In short, use render to make sure the content is available before accessing it, and also remember that call "response.render()" and for "JsonResponse" use "json.dumps(response.content)" directly.

I have rewritten your code and noted down below:


    def wrap_method(self, method):
        @wraps(method)
        def wrapped_method(self, request, *args, **kwargs):
            method_name = request.method.lower()
            if method_name in getattr(self, "methods_to_log", []):
                log = Log(path=request.path, method=method_name)
                log.request_body = Log.parse_request_body(request)
    
                try:
                    response = method(self, request, *args, **kwargs)
                except Exception as e:
                    tb = traceback.format_exc()
                    log.error_traceback = tb
                    log.status_code = 500
                    log.response_body = str(e)
                    log.save()
                    return JsonResponse({"error": str(e)}, status=500)
    
                if isinstance(response, Response):
                    response.render()
    
                if self.log_success or response.status_code >= 400:
                    log.status_code = response.status_code
    
                    if isinstance(response, JsonResponse):
                        log.response_body = response.content.decode("utf-8")
                    elif isinstance(response, Response) and 'application/json' in response.get('Content-Type', ''):
                        log.response_body = response.rendered_content.decode("utf-8")
                    
                    log.save()
    
                return response
    
            return method(self, request, *args, **kwargs)
    
        return wrapped_method

I hope your query has been resolved now, in case you face any difficulties then let me know.

Back to Top