目錄:
- 自定義響應格式介紹
- 自定義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)
后面就在對應的視圖中導入並使用。