I want to overwrite Response class of django rest framework so that response back responsive dictionary contain three parameter message, status and data
Hello dears all
I try to change Response Class in DRF to pass two extra parameter ( message, status ) plus data provide by DRF serializer. message pass the message like Done, User Created or etc and status pass the message like fail or success or etc message and this message useful for reserve special code between frontend and backend.
I want to if don't set this parameter return empty character or null result back to client side
for example in success mode:
{
    'data': {
        'value_one': 'some data',
        'value_two': 'some data',
        'value_three': [
                'value', 'value', 'value'
            ],
        },
    }
    'message': 'Done',
    'status': 'success',
}
and in failure mode:
{
    'data': ['any error message raise by serializer',]
    'message': 'Create User Failed',
    'status': 'failure',
}
I search about my question and found this solution:
if i inheritance DRF Response Class in my class and overwrite __init__ method and get message, data and status in this method and call init of parent with own data structure and use this responsive class in my functionality like this implement:
from rest_framework.response import Response
class Response(Response):
    def __init__(self, data=None, message=None, data_status=None, status=None,
                template_name=None, headers=None,
                exception=False, content_type=None):
        data_content = {
            'status': data_status,
            'data': data,
            'message': message,
        }
        super(Response, self).__init__(
            data=data_content,
            status=status,
            template_name=template_name,
            headers=headers,
            exception=exception,
            content_type=content_type
        )
in success mode call:
return Response(data=serializer.data, message='Done', data_status='success', status=200)
in failure mode call:
return Response(data=serializer.errors, message='Create User Failed', data_status='failure', status=400)
and use own Response class in all views class
we had problem in this solution: if we use GenericViews Class must be overwrite all http methods we used in view's logic and call own class and this is DRY!!
and other solution i found. in serialized layer, we have abstract method def to_representation(self, instance): in Serializer class and implement in other class like ModelSerializer class inheritance Serializer and if we overwrite this method in our serializer class and re fetch data before send to view layer, implement like:
from collections import OrderedDict
class OurSerializer(serializer.ModelSerializer):
....
    def to_representation(self, instance):
        data = super(serializers.ModelSerializer, self).to_representation(instance)
        result = OrderedDict()
        result['data'] = data
        result['message'] = 'Done'
        result['status'] = 'sucssed'
        return result
this solution solve above problem but we have two problem again
one: if we use nested serializer and we had overwrite this function in serializer class return bad data like:
{
    'data': {
        'value_one': 'some data',
        'value_two': 'some data',
        'value_three': {
            'data': [
                'value', 'value', 'value'
            ],
            'message': 'Done',
            'status': 'sucssed',
        },
    }
    'message': 'Done',
    'status': 'sucssed',
}
and message and status repeated and structure not pretty for client
and two: we cant handle exception in this mode and just way to handle exception just with middleware class like this DRF Exception Handling and this isn't useful way, we can't handle any type of error occurs in view and generate comfortable separate message and status.
IF there's another good solution to this question, please guide me.
thanks :)
Serializers in Django REST Framework are responsible for converting objects into data types understandable by javascript and front-end frameworks. Serializers also provide deserialization, allowing parsed data to be converted back into complex types, after first validating the incoming data.
The browsable API feature in the Django REST framework generates HTML output for different resources. It facilitates interaction with RESTful web service through any web browser. To enable this feature, we should specify text/html for the Content-Type key in the request header.
The HyperlinkedModelSerializer class is similar to the ModelSerializer class except that it uses hyperlinks to represent relationships, rather than primary keys. By default the serializer will include a url field instead of a primary key field.
To resolve this, best practice (that DRF has proposed) is to use 'renderer' classes. A renderer manipulates and returns structured response.
Django uses renderers like Template Renderer and DRF benefits this feature and provides API Renderers.
To do so, you could provide such this renderer in a package (e.g. app_name.renderers.ApiRenderer):
from rest_framework.renderers import BaseRenderer
from rest_framework.utils import json
class ApiRenderer(BaseRenderer):
    def render(self, data, accepted_media_type=None, renderer_context=None):
        response_dict = {
            'status': 'failure',
            'data': {},
            'message': '',
        }
        if data.get('data'):
            response_dict['data'] = data.get('data')
        if data.get('status'):
            response_dict['status'] = data.get('status')
        if data.get('message'):
            response_dict['message'] = data.get('message')
        data = response_dict
        return json.dumps(data)
And then in your settings file:
REST_FRAMEWORK = {
    ...
    'DEFAULT_RENDERER_CLASSES': (
        'app_name.renderers.ApiRenderer',
    ),
    ...
}
By this action all views that extend DRF generic views will use renderer. If you needed to override setting you can use renderer_classes attribute for generic view classes and @renderer_classes decorator for api view functions.
A comprehensive renderer class to override is available at <virtualenv_dir>/lib/python3.6/site-packages/rest_framework/renderers.py.
This would be more a more robust solution, as it can be used with Generic Views hassle free.
In case of Generic views, the data argument we receive in the render() method is sent automatically by the generic view itself (if not overriding the methods, which will be against DRY), so we cannot handle it, as it does in the accepted answer.
Also, the checks in render() can be easily altered as per the needs (Eg., handling no-2XX status codes in this solution).
from rest_framework.renderers import JSONRenderer
class CustomRenderer(JSONRenderer):
    def render(self, data, accepted_media_type=None, renderer_context=None):
        status_code = renderer_context['response'].status_code
        response = {
          "status": "success",
          "code": status_code,
          "data": data,
          "message": None
        }
        if not str(status_code).startswith('2'):
            response["status"] = "error"
            response["data"] = None
            try:
                response["message"] = data["detail"]
            except KeyError:
                response["data"] = data
        return super(CustomRenderer, self).render(response, accepted_media_type, renderer_context)
                        Just an addition: I prefer to inherit from JSONRenderer. That way you get the nice formatting and indentation out of the box
    from rest_framework.renderers import JSONRenderer
    
    class CustomRenderer(JSONRenderer):
          
          def render(self, data, accepted_media_type=None, renderer_context=None):
              response = {
                 'error': False,
                 'message': 'Success',
                 'data': data
              }
              return super(CustomRenderer, self).render(response, accepted_media_type, renderer_context)
Then in your views:
    from rest_framework.renderers import BrowsableAPIRenderer
    from api.renderers import CustomRenderer
    class MyViewSet(viewsets.ModelViewSet):
          renderer_classes = [CustomRenderer, BrowsableAPIRenderer]
          
          ...
When used with the BrowsableAPIRenderer as shown above, you get your nicely formatted custom response rendered in DRF's Browsable API
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With