13-2 自定義異常格式和響應格式-DRF自定義響應格式


目錄:

  • 自定義響應格式介紹
  • 自定義JSONResponse
  • 自定義ModelViewSet
  • 自定義分頁類需要修改
  • 自定義Generics

一、自定義響應格式介紹

 之前我們講了自定義異常,主要目的是為了方便移動端開發使用的,但是我們在給移動端提供正常的響應格式的時候,也要像自定義異常一樣,具有一定的格式。目前我們返回的數據格式是這樣的:

{
    "next": "http://127.0.0.1:8000/api/v1/games/?cursor=cD0x",
    "previous": null,
    "results": [
        {
            "id": 1,
            "name": "帥高高",
            "status": 0
        }
    ]
}

或者:

{
    "id": 1,
    "name": "帥高高",
    "status": 0
}

還是那句話,返回這樣格式的數據,對於移動端是不友好的,所以我們需要自定義返回消息,返回的結構如下 :

{
    "code": 200,
    "message": "success",
    "data": [
        {
            "id": 1,
            "name": "帥高高",
            "status": 0
        }
    ],
    "next": "http://127.0.0.1:8000/api/v1/games/?cursor=cD0x",
    "previous": null
}

或者:

{
    "code": 200,
    "message": "success",
    "data": {
        "id": 1,
        "name": "帥高高",
        "status": 0
    }
}

二、 自定義JSONResponse

2.1、目錄結構

說明:其實是一樣的,我們需要新建一個 custom_json_response.py 文件,來自定義我們的 JSONResponse

...
-app06
    -migrations
        ...
    -apps.py
    -admins.py
    -models.py
    -custom_json_response.py  #新建來自定義我們的 JSONResponse
    -custom_model_view_set.py  #新建,自定義ModelViewSet
    -mypagenumberpaginations.py #編輯,分頁功能的修改
    ....
...

2.2、自定義 JSONResponse

說明:在我們新建的custom_json_response.py中 自定義我們的 JSONResponse。

from django.utils import six
from rest_framework.response import Response
from rest_framework.serializers import Serializer

class JsonResponse(Response):
    """
    An HttpResponse that allows its data to be rendered into
    arbitrary media types.
    """

    def __init__(self, data=None, code=None, msg=None,
                 status=None,
                 template_name=None, headers=None,
                 exception=False, content_type=None, **kwargs):
        """
        Alters the init arguments slightly.
        For example, drop 'template_name', and instead use 'data'.
        Setting 'renderer' and 'media_type' will typically be deferred,
        For example being set automatically by the `APIView`.
        """
        super(Response, self).__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 six.iteritems(headers):
                self[name] = value

2.3、APIView使用自定義響應

說明:其實很簡單,APIView中很簡單的,只需要把 Response 使用為JsonResponse:

from app06.custom_json_response import JsonResponse  #導入自定義響應格式

class UserDetail(APIView):
 
    .....
 
    def get(self,request, *args, **kwargs):
        user = self.get_object(kwargs.get('id'))
        ser = UserSerializer(instance=user,context={"request": request})
        return JsonResponse(ser.data, code=200, msg='ok')   #使用自定義JsonResponse格式
 
     ....

三、自定義ModelViewSet

為啥我們需要自定義ModelViewSet吶。因為我們ModelViewSet 默認不是用我們的 自定義的 JsonResponse,而是采用默認的。響應格式已經高度封裝了,所以這邊自己寫一個就是了。首先我們來看看源碼帶,是不是已經封裝好了:

Ctrl + ModelViewSet => CreateModelMixin

來看下:

class CreateModelMixin(object):
    """
    Create a model instance.
    """
    def create(self, request, *args, **kwargs):
        ....
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)  #用默認的

好咧,那我們自己封裝一個,首先創建 custom_model_view_set.py 文件:

from rest_framework import status
from rest_framework import viewsets
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=201, status=status.HTTP_201_CREATED,
                            headers=headers)

    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, code=200, msg="success", status=status.HTTP_200_OK)

    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return JsonResponse(data=serializer.data, code=200, msg="success", 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=200, status=status.HTTP_200_OK)

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

好啦,那這樣,我就使用自己定義的ModelViewSet在視圖中繼承使用:

from .custom_model_view_set import CustomModelViewSet  #導入自定義的

class GameView(CustomModelViewSet):  #繼承自定義的

    queryset = Game.objects.all()
    serializer_class = GameSerializer

效果圖如下:

四、自定義分頁類需要修改

 我們在分頁的時候,沒有使用自定義的 JsonRespose,導致如下輸出:

那這個不是我們想要的,所以我們需要自定義分頁類需要修改,編輯 mypagenumberpaginations.py文件:

from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination
from .custom_json_response import JsonResponse
from rest_framework import status


#普通分頁
class MyPageNumberPagination(PageNumberPagination):
    page_size = 1
    max_page_size = 1
    page_size_query_param = 'size'
    page_query_param = 'page'

    def get_paginated_response(self, data):
        return JsonResponse(data=data, code=200, msg="success", status=status.HTTP_200_OK, next=self.get_next_link(),
                            previous=self.get_previous_link(), count=self.page.paginator.count)

#切割分頁
class MyPageNumberPagination(LimitOffsetPagination):
    default_limit = 1
    limit_query_param = 'limit'
    offset_query_param = 'offset'
    max_limit = 2

    def get_paginated_response(self, data):
        return JsonResponse(data=data, code=200, msg="success", status=status.HTTP_200_OK, next=self.get_next_link(),
                            previous=self.get_previous_link(), count=self.count)

#加密分頁
class MyPageNumberPagination(CursorPagination):
    cursor_query_param = 'cursor'
    page_size = 1
    ordering = 'id'
    page_size_query_param = 'size'
    max_page_size = 1

    def get_paginated_response(self, data):
        return JsonResponse(data=data, code=200, msg="success", status=status.HTTP_200_OK, next=self.get_next_link(),
                            previous=self.get_previous_link())

注意:根據需求選擇一種分頁方式。我們這邊選擇普通分頁來測試:

哈哈,是我們想要的啦。

五、自定義Generics

其實自定義Generics跟我們的 ModelViewSet是一樣的效果,這邊就廢話不多說了,直接上代碼吧。原理跟ModelViewSet一樣。

from rest_framework import status
from rest_framework import viewsets
from .custom_json_response import JsonResponse
from rest_framework import generics


class ListCreateAPIView(generics.ListCreateAPIView):

    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=201, status=status.HTTP_201_CREATED,
                            headers=headers)

    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, code=200, msg="success", status=status.HTTP_200_OK)


class RetrieveUpdateDestroyAPIView(generics.RetrieveUpdateDestroyAPIView):

    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return JsonResponse(data=serializer.data, code=200, msg="success", 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=200, status=status.HTTP_200_OK)

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

后面就在對應的視圖中導入並使用。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM