question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

How to set a custom response body format globally?

See original GitHub issue

Hi, friends.I am using drf-spectacular==0.17.1 in a Django project(Django==3.2.4 & djangorestframework==3.12.4) to generate API documentation. I have a question and want to ask for help

  1. I customized the ModelViewSet. Use a custom response body format(JsonResponse). As follows
from rest_framework import status
from rest_framework import viewsets
from rest_framework import mixins
from rest_framework.viewsets import GenericViewSet

from .custom_json_response import JsonResponse


class CustomModelViewSet(viewsets.ModelViewSet):
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return JsonResponse(data=serializer.data, msg='success', code=20000, status=status.HTTP_201_CREATED,
                            headers=headers)

    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return JsonResponse(data=serializer.data, msg='success', code=20000, status=status.HTTP_200_OK)

    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        if getattr(instance, '_prefetched_objects_cache', None):
            # If 'prefetch_related' has been applied to a queryset, we need to
            # forcibly invalidate the prefetch cache on the instance.
            instance._prefetched_objects_cache = {}

        return JsonResponse(data=serializer.data, msg='success', code=20000, status=status.HTTP_200_OK)

    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        self.perform_destroy(instance)
        return JsonResponse(data=[], msg='delete resource success', code=20000, status=status.HTTP_204_NO_CONTENT)

    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return JsonResponse(data=serializer.data, msg='success', code=20000, status=status.HTTP_200_OK)


class MyListRetrieveDestroyModelViewSet(mixins.RetrieveModelMixin,
                                        mixins.ListModelMixin,
                                        mixins.DestroyModelMixin,
                                        GenericViewSet):
    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return JsonResponse(data=serializer.data, msg='success', code=20000, status=status.HTTP_200_OK)

    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        self.perform_destroy(instance)
        return JsonResponse(data=[], msg='delete resource success', code=20000, status=status.HTTP_204_NO_CONTENT)

    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return JsonResponse(data=serializer.data, msg='success', code=20000, status=status.HTTP_200_OK)


class MyListRetrieveModelViewSet(mixins.RetrieveModelMixin,
                                 mixins.ListModelMixin,
                                 GenericViewSet):
    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return JsonResponse(data=serializer.data, msg='success', code=20000, status=status.HTTP_200_OK)

    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return JsonResponse(data=serializer.data, msg='success', code=20000, status=status.HTTP_200_OK)
  1. The content of JsonResponse is as follows(see the self.data = {'code': code, 'message': msg, 'data': data})
from rest_framework.response import Response
from rest_framework.serializers import Serializer


class JsonResponse(Response):
    def __init__(self, data=None, code=None, msg=None,
                 status=None,
                 template_name=None, headers=None,
                 exception=False, content_type=None, **kwargs):
        super().__init__(None, status=status)

        if isinstance(data, Serializer):
            msg = (
                'You passed a Serializer instance as data, but '
                'probably meant to pass serialized `.data` or '
                '`.error`. representation.'
            )
            raise AssertionError(msg)
        self.data = {'code': code, 'message': msg, 'data': data}
        self.data.update(kwargs)
        self.template_name = template_name
        self.exception = exception
        self.content_type = content_type

        if headers:
            for name, value in headers.items():
                self[name] = value
  1. I extends the CustomModelViewSet class in views.py to achieve a unified response body format, as shown below
@extend_schema(tags=['项目管理'])
class ProjectsViewSet(CustomModelViewSet):
    serializer_class = ProjectSerializer
    permission_classes = [permissions.IsAuthenticated, IsObjectCreatorOrModifierInRequestUserGroups]
    filterset_fields = ['project_name', 'project_desc', 'creator', 'modifier']
  1. The actual response body of the interface request is as follows:
{
    "code": 20000,
    "message": "success",
    "data": {
        "id": 1,
        "create_time": "2021-06-14 13:54:26",
        "update_time": "2021-06-14 13:54:26",
        "creator": "anonymous",
        "modifier": "anonymous",
        "project_name": "测试项目1",
        "project_desc": "测试项目description"
    }
}
  1. But viewing the API document in the browser, the format of the response body is not displayed like{"code": 20000, "message": "xxx", "data": "xxx"} image image
  2. So I want to ask, how to set the Schema of the unified response body format ({"code": 20000, "message": "xxx", "data": "xxx"}) globally? thank you very much.

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:7 (2 by maintainers)

github_iconTop GitHub Comments

2reactions
tfranzelcommented, Oct 10, 2021
1reaction
hashqueuecommented, Oct 10, 2021

hey @passerby223, sry i was unable to get around looking at this yet. apparently you found a solution. i suspect that this either required subclassing AutoSchema or doing a POSTPROCESSING_HOOK as this is a non-standard behavior.

Yes, I have found a solution, thank you very much!

兄弟,你最后是怎么处理的?bro, could you please tell me about your solution, I’ll appreciate it 🎉

see issue #49

Read more comments on GitHub >

github_iconTop Results From Across the Web

Format response data in ASP.NET Core Web API
ASP.NET Core MVC supports formatting response data, using specified formats or in response to a client's request.
Read more >
Spring configure @ResponseBody JSON format
For the folks who are using Java based Spring configuration: @Configuration @ComponentScan(basePackages = "com.domain.sample") @EnableWebMvc public class ...
Read more >
Customize HTTP Responses | Documentation
You can use a Global Request Filter to set Custom HTTP Headers and then short-circuit the request: public override void Configure(Container container) ...
Read more >
Custom responses for Block actions - AWS Documentation
In the individual Block action settings, you reference the custom body that you want to use and you define the status code and...
Read more >
Formatting Response Data - ASP.NET Core Documentation
You can use a tool like Fiddler to create a request that includes an Accept header and specify another format. In that case,...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found