Как получить содержимое ответа из объекта Response() в декораторе представления?

Допустим, у меня есть декоратор, который может быть применен к представлению, основанному на классе DRF. Базовая версия декоратора выглядит следующим образом:-

`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`

Декоратор применяется к классу представления следующим образом:-

`@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")`

Проблема в том, что когда log_success равен true и я хочу регистрировать успешные ответы или 400 ошибок, я получаю ошибки типа JsonResponse' object has no attribute 'rendered_content' or 'Response' object has no attribute 'rendered_content' depending on what kind of response I am sending.

Исключения регистрируются отлично, остальное - нет. Есть идеи, как это исправить?

Я попробовал установить accepted_renderer вручную, и это не дает ошибки. Но при этом содержимое ответа сохраняется в виде пустой строки. Есть идеи, как это исправить?

Вы столкнулись с этой проблемой в основном потому, что JsonResponse и Django REST Framworks объекты ответа, действуют совершенно по-разному в плане рендеринга содержимого ответа.

Если говорить конкретно об объекте DRF Response, то rendered_content является хорошим вариантом, он заполняется только после того, как ответ отображается. Для JsonResponse этот атрибут отсутствует, что приводит к ошибкам.

Короче говоря, используйте render, чтобы убедиться, что содержимое доступно, прежде чем получить к нему доступ, а также помните, что вызывать "response.render()" и для "JsonResponse" использовать "json.dumps(response.content)" напрямую.

Я переписал ваш код и отметил следующее:


    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

Надеюсь, ваш вопрос уже решен, если у вас возникнут какие-либо трудности, дайте мне знать.

Вернуться на верх