Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a custom JSON payload structure in Django Rest Framework?

I'm creating an API that requires custom structure for all JSON responses.

{
    code: 200,
    payload: [
        {...},
        {...},
        {...}
    ]
}

Where payload contains all the items returned by the query.

I've created a custom Renderer that extends JSONRenderer but in order to access the response code I need to access the render_context.

class VendorRenderer(JSONRenderer):
    def render(self, data, accepted_media_type=None, render_context=None):
        response = render_context['response']
        data = {
            'code': response.status_code,
            'payload': data
        }
        return super(VendorRenderer, self).render(data, accepted_media_type, render_context)

Is this the right place to be doing this kind of wrapping or should it be taking place somewhere else like the ViewSet or by extending the Response object?

like image 597
Soviut Avatar asked May 16 '16 19:05

Soviut


1 Answers

This is the correct place to wrap the response because responses are rendered after error checking is performed in the middleware.

Doing it in the view may cause unexpected results as, for example a serialization error won't be caught correctly by your code parameter and wrapping it in a custom serializer won't have access to the response object. Also, you may want authentication or throttling errors to be wrapped in your response.

Be aware that any pagination that happens is also going to be wrapped in your payload parameter:

{
    "code":200,
    "payload": {
        "count": 20,
        "next": 2,
        "previous": 0,
        "results": [
            ...
        ]
    }
}

One solution to this is to implement pagination using HTTP headers.

from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response 

class StandardResultsSetHeaderPagination(PageNumberPagination):
    page_size = 10
    page_size_query_param = 'page_size'
    max_page_size = 1000

    def get_paginated_response(self, data):
        headers = {
            'X-Count':    self.page.paginator.count,
            'X-Next':     self.get_next_link(),
            'X-Previous': self.get_previous_link()
        }
        return Response(data, headers=headers)
like image 153
Daniel van Flymen Avatar answered Sep 22 '22 03:09

Daniel van Flymen