Django Rest Framework-例外、戻り値処理とページング実装


一.異常
Django Rest Frameworkを使用する場合、異常が発生した場合は、次のようになります.
{"detail": "Not allowed."}

しかし、バックグラウンドでは一般的なパターンが求められます.
{
    "desc":"Not allowed.",
    "code":400,
    "data":null
    }

公式サイトのドキュメントはまだはっきりしています.例外処理をカスタマイズし、構成すればいいです.例えば、次のようにします.
1.実現
from rest_framework.views import exception_handler


def custom_exception_handler(exc, context):
    # Call REST framework's default exception handler first,
    # to get the standard error response.
    response = exception_handler(exc, context)

    # Now add the HTTP status code to the response.
    if response is not None:
        response.data['code'] = response.status_code
        response.data['desc'] = response.data['detail']
        #response.data['data'] = None #    
        del response.data['detail'] #  detail  

    return response

2.構成
settings.pyでの構成
REST_FRAMEWORK = {
     ...
     ...
    'EXCEPTION_HANDLER': (
        'dataAPI.common.api_exception.custom_exception_handler'
    )
     #'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler'
}

二.戻り値
異常に示すように、通常の戻り値も同じタイプです.たとえば、以下に示すように、基本データフォーマットとページングフォーマット(後述)
{
    "desc":"page success", #    
    "code":200, #      2      
    "data":{
        "detail":Array[2], #       
        "total":6, #    
        "page":2 #    
    }
}

Django Rest Frameworkでシーケンス化すると、直接データが返され、私たちが望んでいるデータに合わないので、カスタムResponseでデータが返されます.例えば、APIViewを前提にしています.
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, desc=None,
                 status=None,
                 template_name=None, headers=None,
                 exception=False, content_type=None):
        """
        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, "desc": desc, "data": data}
        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

Example :
def get(self, request, house_pk):
        house = get_object_or_404(House, pk=house_pk) #    
        data = HouseSerializer(house) #   
        return api_response.JsonResponse(data=data.data, code=status.HTTP_200_OK, desc='get house success') #         

結果:
success:JsonResponseが正常に使用されました.
{
  "desc": "get house success",
  "code": 200,
  "data": {
        "pk": 7,
        "name": "VVVVVVV",
        "staff": {
          "phone": "xxxxxxxxxx",
          "username": "yuan"
        }
   }   
}

fail:失敗したのは上の異常で処理:custom_exception_handler
{
  "code": 404,
  "desc": "   。"
}

三.ページング実装
結果の戻り値に示すように、多くの使用にはページング機能が必要ですが、Django Rest Frameworkが持つページング機能は、mixins.ListModelMixin and generics.GenericAPIView classesでこの2つのクラスを継承してこそ使用できますが、柔軟性のためにAPIViewを継承して実現することが多いので、自分でページング機能を使用する必要があります.
データの基本フォーマット:
{
    "desc":"page success", #    
    "code":200, #      2      
    "data":{
        "detail":Array[2], #       
        "total":6, #    
        "page":2 #    
    }
}

ここではapiインタフェースを使用します.たとえば、page_sizeの最大数など、他の検証を追加できます.
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
from rest_framework import status

from dataAPI.common import api_response #           JsonResponse


def api_paging(objs, request, Serializer):
    """
    objs :     
    request :     
    Serializer :           
    """
    try:
        page_size = int(request.GET.get('page_size', 2))
        page = int(request.GET.get('page', 1))
    except (TypeError, ValueError):
        return api_response.JsonResponse(code=status.HTTP_400_BAD_REQUEST, desc='page and page_size must be integer!')

    paginator = Paginator(objs, page_size) # paginator  
    total = paginator.num_pages #   
    try:
        objs = paginator.page(page)
    except PageNotAnInteger:
        objs = paginator.page(1)
    except EmptyPage:
        objs = paginator.page(paginator.num_pages)

    serializer = Serializer(objs, many=True) #     

    return api_response.JsonResponse(data={
        'detail': serializer.data,
        'page': page,
        'total': total
    }, code=status.HTTP_200_OK, desc='page success') #  

Example :
def get(self, request, format=None):
        """
          page_size : ?page=1&page_size=10
          page :
        """
        farms = self.get_object_list() #    
        return api_paginator.api_paging(farms, request, FarmSerializer) #    ,   

結果:
{
  "desc": "page success",
  "code": 200,
  "data": {
    "detail": [
      {
        "name": "V3",
      },
      {
        "name": "V2",
      }
    ],
    "total": 6, #   
    "page": 2 #   
  }
}

完了