Customize Json Response in django rest framework

Viewed 8210

Given below is my serializer class. I have all fields in one model.I would like to change the representation of serializer data in custom format. Tried to_representation method of serializer but could not success.

class MyListSerilizer(ModelSerializer):
  class Meta:
    model=MyModel
    fields=['Name','Address_ID','Portal','Address','City','DisplayOrderDateTime','Email','Order_ID','Order_Code','Item_Code','DispatchedOn','Payment_Mode','Shipping_Charge','ShippingMethodCode','ShippingMethodCharges','CashOnDeliveryCharges','CurrencyCode','BillingAddress','GiftWrapCharges','SaleOrderItemCode','Shipping_ref','Cancellable','OnHold','Quantity','Invoice_No''Portal',........]

So in my view class defined and output corresponding to them also mentioned over here.

   class MyListAPIView(ListAPIView):
      def list(self,request):
          queryset=MyModel.objects.all()
          serializer=MyListSerilizer(queryset,many=True)
          return Response({'Records':serializer.data})

Output :---------->Corresponding to view is

  "Records": [
    {
        "Name": "abc",
        "Address_ID": "6819319",
        "Portal": "amksl",
        "Address": "",
        "City": "absjsls",
        "DisplayOrderDateTime": null,
        "Email": "abcd@gmail.com",
        "Order_ID": "",
        "Order_Code": "",
        "Item_Code": "",
        "DispatchedOn": "",
        "Payment_Mode": ""
     },
      {
         "Name": "abc",
        "Address_ID": "6819319",
        "Portal": "amksl",
        "Address": "",
        "City": "absjsls",
        "DisplayOrderDateTime": null,
        "Email": "abcd@gmail.com",
        "Order_ID": "",
        "Order_Code": "",
        "Item_Code": "",
        "DispatchedOn": "",
        "Payment_Mode": ""
     },
      so on....

so my question is how would i achieve this json format. In short how do i customize my view class

     {
      "identifiers":{
                "email":"abcd@gmai.com",
                "phone":"123664"
              },
"activity_type": "purchase",
"timestamp": "UNIX TIMESTAMP",                
"products": [{
                "brandName": "abc",
                "id": "1",                                  
                "sku": "abcd",                                
                "name": "mnis",                           
                "price": 12.9,
                "discount": "",
                "quantity": "",
                "currency": ""
                 }]
"cart_info":{
                "total":"",
                "revenue":"",
                "currency":""
            },
"Order_info":{
               "total":"2121",
                .
                .
                .
             }
  },
   {
      "identifiers":{
                "email":"abcd@gmai.com",
                "phone":"123664"
              },
"activity_type": "purchase",
"timestamp": "UNIX TIMESTAMP",                
"products": [{
                "brandName": "abc",
                "id": "1",                                  
                "sku": "abcd",                                
                "name": "mnis",                           
                "price": 12.9,
                "discount": "",
                "quantity": "",
                "currency": ""
                 }]
"cart_info":{
                "total":"",
                "revenue":"",...so on
3 Answers

override to_representation method in the serializer class

class MyListSerilizer(ModelSerializer):

     class Meta:
        model=MyModel
        fields=['Name','Address_ID','Portal','Address','City','DisplayOrderDateTime','Email','Order_ID','Order_Code','Item_Code','DispatchedOn','Payment_Mode','Shipping_Charge','ShippingMethodCode','ShippingMethodCharges','CashOnDeliveryCharges','CurrencyCode','BillingAddress','GiftWrapCharges','SaleOrderItemCode','Shipping_ref','Cancellable','OnHold','Quantity','Invoice_No''Portal',........]


     def to_representation(self, instance):
         # instance is the model object. create the custom json format by accessing instance attributes normaly and return it


        identifiers = dict()
        identifiers['email'] = instance.Email
        identifiers['phone'] = instance.phone

        representation = {
            'identifiers': identifiers,
            'activity_type': instance.xxxx,
            'timestamp': instance.xxxxx,
            .
            .
            .  -> your custom data
         } 

     return representation

whenever you call serializer.data, this representation dictionary will be returned

Create a file renderers.py anywhere in the project and write the following code:

from bson import ObjectId
from rest_framework import status
from rest_framework.renderers import JSONRenderer
from rest_framework.utils import encoders


def get_status(code):
    """Get the human readable SNAKE_CASE version of a status code."""
    for name, val in status.__dict__.items():
        if not callable(val) and code is val:
            return name.replace("HTTP_%s_" % code, "")
    return "UNKNOWN"


class BaseAPIJSONRenderer(JSONRenderer):
    def render(self, data, accepted_media_type=None, renderer_context=None):
        """
        Custom API response format for all APIs
        Example success:
        {
            "code": 200,
            "status": "OK",
            "data": {
                "key": "value"
            }
        }

        Example error:
        {
            "code": 404,
            "status": "NOT_FOUND",
            "errors": [
                {
                    "detail": "Not found."
                }
            ]
        }
        """
        response = renderer_context["response"]

        # Modify the response into a cohesive response format
        modified_data = {}
        modified_data["code"] = response.status_code
        modified_data["status"] = get_status(response.status_code)
        if status.is_client_error(response.status_code) or status.is_server_error(
            response.status_code
        ):
            modified_data["errors"] = data
        else:
            modified_data["data"] = data

        return super().render(modified_data, accepted_media_type, renderer_context)

class MyJSONEncoder(encoders.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, ObjectId):
            return str(obj)
        return encoders.JSONEncoder.default(self, obj)

def get_error_message(error_dict):
    response = error_dict[next(iter(error_dict))]
    error_name = next(iter(error_dict))
    if isinstance(response, dict):
        response = get_error_message(response)
    elif isinstance(response, list):
        response_message = response[0]
        if isinstance(response_message, dict):
            response = get_error_message(response_message)
        else:
            response = response[0]

    return response.replace("This", error_name)
class MyJSONRenderer(JSONRenderer):
    encoder_class = MyJSONEncoder

    def render(self, data, accepted_media_type=None, renderer_context=None):
        """
        Modify API response format.
        Example success:
        {
            "code": 200,
            "status": "OK",
            "message": "updated succesfully",
            "data": {
                "name": "name"
            }
        }

        Example error:
        {
            "code": 404,
            "status": "NOT_FOUND",
            "message": "Not Found"
        }
        """
        response = renderer_context["response"]

        # Modify the response into a cohesive response format
        modified_data = {}
        modified_data["code"] = response.status_code
        modified_data["status"] = get_status(response.status_code)

        if status.is_client_error(response.status_code) or status.is_server_error(
            response.status_code
        ):
            if isinstance(data, list) and data:
                if isinstance(data[0], dict):
                    message = (get_error_message(data),)
                elif isinstance(data[0], str):
                    message = data[0]
            if isinstance(data, dict):
                message = get_error_message(data)
            modified_data["message"] = message
        else:
            if isinstance(data, dict):
                message = data.pop("message_", None)
                if message:
                    modified_data["message"] = message
                if data.get("results") is not None:

                    modified_data["count"] = data.get("count")
                    modified_data["next"] = data.get("next")
                    modified_data["previous"] = data.get("previous")
                    modified_data["data"] = data.get("results")
                    # if data.get("page"):
                    #     modified_data["page"] = data.get("page")
                    # else:
                    #     modified_data["last_id"] = data.get("last_id")
                    # modified_data["last_page"] = data.get("last_page")
                else:
                    modified_data["data"] = data
            else:
                modified_data["data"] = data
        

        return super().render(modified_data, accepted_media_type, renderer_context)

After creating the file defined in a setting like this:

"DEFAULT_RENDERER_CLASSES": [
        "app_modules.apis.base.renderers.MyJSONRenderer",
    ],

app_modules.apis.base its a file path

Related