1. 程式人生 > 實用技巧 >drf請求、響應與檢視

drf請求、響應與檢視

目錄

一、請求

1 定義

REST framework 傳入檢視的request物件不再是Django預設的HttpRequest物件,而是REST framework提供的擴充套件了HttpRequest類的Request

類的物件。

由於這個Request類內部重寫了__getattr__方法,所以原來的request類的方法在新的request也可以點出來使用

2 常用屬性

1).data

request.data 返回解析之後的請求體資料。類似於Django中標準的request.POSTrequest.FILES屬性,但提供如下特性:

  • 包含了解析之後的檔案和非檔案資料
  • 包含了對POST、PUT、PATCH請求方式解析後的資料
  • 利用了REST framework的parsers解析器,不僅支援表單型別資料,也支援JSON資料

2).query_params

request.query_params與Django標準的request.GET

相同,只是更換了更正確的名稱而已。

總結:在drf中基本上使用的資料都是request.data,因為django原生的request不會解析json資料

二、響應

1 Response

rest_framework.response.Response

drf提供了一個響應類Response,該類在構造響應物件的時候,響應會被轉化(render)成符合前端需求的型別,這一步在drf重寫的dispatch原始碼中有。

但是我們可以發現,如果我們是瀏覽器發起請求得到的是一個頁面,如果是postman發起請求,得到的直接就是json資料,這是內部幫我們判斷了一下。

drf也提供了修改預設值的方法,讓我們可以在前端也直接接受json格式的資料

# 在settings.py中新增(這是預設的,不加也是這樣)
# 預設原來的出處在drf的settings裡
# 當發起響應的時候,會先在我們專案的settings內找,找不到再去預設的裡找
REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': (  # 預設響應渲染類
        # 註釋掉下面任意一條就只能返回另一條了
        'rest_framework.renderers.JSONRenderer',  # json渲染器
        'rest_framework.renderers.BrowsableAPIRenderer',  # 瀏覽API渲染器
    )
}

2 構造方法

Response(data, status=None, template_name=None, headers=None, content_type=None)

引數說明:

  • data: 為響應準備的序列化處理後的資料,通常放一個字典;
  • status: 狀態碼,預設200;
  • template_name: 模板名稱,如果使用HTMLRenderer 時需指明,通常不用;
  • headers: 用於存放響應頭資訊的字典,放一個字典;
  • content_type: 響應資料的Content-Type,通常此引數無需傳遞,REST framework會根據前端所需型別資料來設定該引數。

3 狀態碼

為了方便設定狀態碼,REST framewrok在rest_framework.status模組中提供了常用狀態碼常量。

1)資訊告知 - 1xx

HTTP_100_CONTINUE
HTTP_101_SWITCHING_PROTOCOLS

2)成功 - 2xx

HTTP_200_OK
HTTP_201_CREATED
HTTP_202_ACCEPTED
HTTP_203_NON_AUTHORITATIVE_INFORMATION
HTTP_204_NO_CONTENT
HTTP_205_RESET_CONTENT
HTTP_206_PARTIAL_CONTENT
HTTP_207_MULTI_STATUS

3)重定向 - 3xx

HTTP_300_MULTIPLE_CHOICES
HTTP_301_MOVED_PERMANENTLY
HTTP_302_FOUND
HTTP_303_SEE_OTHER
HTTP_304_NOT_MODIFIED
HTTP_305_USE_PROXY
HTTP_306_RESERVED
HTTP_307_TEMPORARY_REDIRECT

4)客戶端錯誤 - 4xx

HTTP_400_BAD_REQUEST
HTTP_401_UNAUTHORIZED
HTTP_402_PAYMENT_REQUIRED
HTTP_403_FORBIDDEN
HTTP_404_NOT_FOUND
HTTP_405_METHOD_NOT_ALLOWED
HTTP_406_NOT_ACCEPTABLE
HTTP_407_PROXY_AUTHENTICATION_REQUIRED
HTTP_408_REQUEST_TIMEOUT
HTTP_409_CONFLICT
HTTP_410_GONE
HTTP_411_LENGTH_REQUIRED
HTTP_412_PRECONDITION_FAILED
HTTP_413_REQUEST_ENTITY_TOO_LARGE
HTTP_414_REQUEST_URI_TOO_LONG
HTTP_415_UNSUPPORTED_MEDIA_TYPE
HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE
HTTP_417_EXPECTATION_FAILED
HTTP_422_UNPROCESSABLE_ENTITY
HTTP_423_LOCKED
HTTP_424_FAILED_DEPENDENCY
HTTP_428_PRECONDITION_REQUIRED
HTTP_429_TOO_MANY_REQUESTS
HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE
HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS

5)伺服器錯誤 - 5xx

HTTP_500_INTERNAL_SERVER_ERROR
HTTP_501_NOT_IMPLEMENTED
HTTP_502_BAD_GATEWAY
HTTP_503_SERVICE_UNAVAILABLE
HTTP_504_GATEWAY_TIMEOUT
HTTP_505_HTTP_VERSION_NOT_SUPPORTED
HTTP_507_INSUFFICIENT_STORAGE
HTTP_511_NETWORK_AUTHENTICATION_REQUIRED

三、檢視

1 基類檢視 APIView

apiview是drf中所有檢視類的基類,它繼承自djangoview

在前幾篇中有對應的原始碼分析,apiview和view的區別:

  • 傳入到檢視類中的request是drf新封裝的,是在原來request上擴充套件的,所以原來的屬性方法也能直接使用
  • 返回的response,會在apiview內部渲染成符合前端要求的格式(json或者html)
  • 任何APIException都會被捕獲,並且處理成合適的響應資訊
  • 在執行apiview內的dispatch方法,會對請求身份認證,許可權檢查,流量控制

支援定義的類屬性

  • authentication_classes 列表或元祖,身份認證類
  • permissoin_classes 列表或元祖,許可權檢查類
  • throttle_classes 列表或元祖,流量控制類

APIView實現5個常用的api介面

class StudentAPI(APIView):
    def get(self,request,pk):
        obj = models.Student.objects.filter(pk = pk).first()
        ser_boj = ser.StudentSerializer(obj)
        return JsonResponse(ser_boj.data)

    def put(self,request,pk):
        back_dic = {'code':200,'msg':'成功'}
        obj = models.Student.objects.filter(pk = pk).first()
        ser_obj = ser.StudentSerializer(obj,request.data)
        if ser_obj.is_valid():
            ser_obj.save()
            back_dic['data'] = ser_obj.data
        else:
            back_dic['code'] = 100
            back_dic['msg'] = '出錯'
            back_dic['error_msg'] = ser_obj.errors
        return JsonResponse(back_dic)
    def delete(self,request,pk):
        obj = models.Student.objects.filter(pk=pk).first()
        if obj:
            obj.delete()
            back_dic = {'code': 200, 'msg': '成功','data':{}}
        else:
            back_dic = {'code': 100, 'msg': '失敗','error_msg':'使用者不存在'}
        return Response(back_dic)


class StudentsAPI(APIView):
    def post(self,request):
        # print(request.data)
        back_dic = {'code': 200, 'msg': '成功'}
        obj = ser.StudentSerializer(data = request.data)
        if obj.is_valid():
            obj.save()
            back_dic['data'] = obj.data
        else:
            back_dic['code'] = 100
            back_dic['msg'] = '錯誤'
            back_dic['error_msg'] = obj.errors

        return (back_dic)

    def get(self,request):
        obj = models.Student.objects.all()
        obj_ser = ser.StudentSerializer(obj,many=True)
        back_dic = {'code':200,'msg':'正確','data':obj_ser.data}
        return Response(back_dic,status=201)

2 通用檢視類 GenericAPIView

繼承自APIView,主要增加了統一檢視類的查詢集和序列化器,作為給下面的Mixin擴充套件類的執行提供方法支援,通常在使用的時候回搭配Mixin擴充套件類使用

內部提供的關於序列化器的屬性和方法

  • 屬性:serializer_class指明檢視使用的序列化器

  • 這個屬性對應的方法:

    • get_serializer_class(self)

    • 這個方法返回的是是序列化器類,如果我們這個檢視類中要根據不同情況使用不同的序列化器類的時候即可重寫這個方法

    • def get_serializer_class(self):
          if self.request.user.is_staff:
              return FullAccountSerializer
          return BasicAccountSerializer
      
    • 這個方法如果不重寫,預設會返回我們在屬性裡定義的serializer_class指定的序列化器類

    • get_serializer(self, args, *kwargs)

    • 這個方法返回的是序列化器物件,主要用來提供給後面介紹的Mixin擴充套件類使用,如果我們想在檢視類中獲取序列化器物件,也可以直接使用這個方法

    • 內部傳參和呼叫序列化器類一樣

    • stu_ser = self.get_serializer(instance = self.get_queryset(),many = True)
      # data 接受修改的資料
      
    • 注意,該方法在提供序列化器物件的時候,會向序列化器物件的context屬性補充三個資料:request、format、view,這三個資料物件可以在定義序列化器時使用。

      • request 當前檢視的請求物件
      • view 當前請求的類檢視物件
      • format 當前請求期望返回的資料格式
  • 屬性:queryset:指明使用的資料查詢集

  • 方法:get_queryset(self)

    • 返回檢視使用的查詢集,通常提供給Mixin擴充套件類使用

    • 若返回的模型物件不存在,返回404

    • 該方法會預設使用APIView提供的check_object_permissions方法檢查當前物件是否有許可權被訪問。

      舉例:

      # url(r'^books/(?P<pk>\d+)/$', views.BookDetailView.as_view()),
      class BookDetailView(GenericAPIView):
          queryset = BookInfo.objects.all()
          serializer_class = BookInfoSerializer
      
          def get(self, request, pk):
              book = self.get_object() # get_object()方法根據pk引數查詢queryset中的資料物件
              serializer = self.get_serializer(book)
              return Response(serializer.data)
      

    其他可以設定的屬性

  • pagination_class 指明分頁控制類

  • filter_backends 指明過濾控制後端

基於GenericAPIView寫5個api

# get全部,post單個
class StudentsGenericAPI(GenericAPIView):
    queryset = models.Student.objects.all()
    serializer_class = ser.StudentModelSerializer
    def get(self,request):
        stu_ser = self.get_serializer(instance = self.get_queryset(),many = True)
        return Response(stu_ser.data)
    def post(self,request):
        print(request.data)
        stu_ser = self.get_serializer(data=request.data)
        if stu_ser.is_valid():
            stu_query = stu_ser.save()
            stu_ser = self.get_serializer(stu_query)
            return Response(stu_ser.data)
        return Response(stu_ser.errors)

# get單個,put單個,del單個
class StudentGenericAPI(GenericAPIView):
    queryset = models.Student.objects
    serializer_class = ser.StudentModelSerializer
    def get(self,request,pk):
        stu_ser = self.get_serializer(self.get_queryset().filter(pk=pk).first())
        return Response(stu_ser.data)
    def put(self,request,pk):
        stu_ser = self.get_serializer(self.get_queryset().filter(pk=pk).first(),data=request.data)
        if stu_ser.is_valid():
            stu_ser.save()
            return Response(stu_ser.data)
        return Response(stu_ser.errors)
    def delete(self,request,pk):
        self.get_queryset().filter(pk=pk).first().delete()
        return Response({'msg':'刪除成功'})

序列化器類:

from rest_framework import serializers

from students.models import Student

class StudentModelSerializer(serializers.ModelSerializer):
    class Meta:
        model= Student
        fields = "__all__"


class StudentModel2Serializer(serializers.ModelSerializer):
    class Meta:
        model= Student
        fields = ("name","class_null")

3 擴充套件類Mixin

# 基於檢視擴充套件類寫的api
# 相比於只用Generic,把5個api方法都在對應的檢視擴充套件類中寫了,只要繼承呼叫即可
# 對應關係如下
# list:get全部,create:post,update:put,destroy:delete,retrieve:get單個
from rest_framework.mixins import ListModelMixin,CreateModelMixin,UpdateModelMixin,DestroyModelMixin,RetrieveModelMixin

class Students2API(GenericAPIView,ListModelMixin,CreateModelMixin):
    queryset = models.Student.objects
    serializer_class = ser.StudentModelSerializer
    def get(self,request):
        return self.list(request)
    def post(self,request):
        return self.create(request)
class Student2API(GenericAPIView,UpdateModelMixin,DestroyModelMixin,RetrieveModelMixin):
    queryset = models.Student.objects
    serializer_class = ser.StudentModelSerializer
    def put(self,request,pk):
        return self.update(request,pk)
    def get(self,request,pk):
        return self.retrieve(request,pk)
    def delete(self,request,pk):
        return self.destroy(request,pk)

4 GenericAPIView的檢視子類

#GenericAPIView的檢視子類 9個
from rest_framework.generics import CreateAPIView,ListAPIView,UpdateAPIView,RetrieveAPIView,DestroyAPIView,ListCreateAPIView,RetrieveUpdateDestroyAPIView,RetrieveDestroyAPIView,RetrieveUpdateAPIView
# 檢視子類在5個擴充套件類的基礎上,把我們函式的方法寫到內部去了
# 所以下面我們只要繼承這些檢視類,然後定義查詢集和序列化器就能根據請求的不同自動呼叫對應的檢視函式
# 對應關係如下
# 單個的對應關係,list:get全部,create:post,update:put,destroy:delete,retrieve:get單個
# 多個的對應關係,ListCreate:get全部+post,RetrieveUpdateDestroy:get單個+put+delete
# 多個的對應關係,RetrieveDestroy:get單個+delete,RetrieveUpdate:get單個+put
class Students3API(ListCreateAPIView):
    queryset = models.Student.objects
    serializer_class = ser.StudentModelSerializer
class Student3API(RetrieveUpdateDestroyAPIView):
    queryset = models.Student.objects
    serializer_class = ser.StudentModelSerializer
    

5 檢視集

1) ViewSet

繼承自APIViewViewSetMixin,作用也與APIView基本類似,提供了身份認證、許可權校驗、流量管理等。

ViewSet主要通過繼承ViewSetMixin來實現在呼叫as_view()時傳入字典(如{'get':'list'})的對映處理工作。

在ViewSet中,沒有提供任何動作action方法,需要我們自己實現action方法。

總結:擴充套件性高,自動化程度低

2)GenericViewSet

使用ViewSet通常並不方便,因為list、retrieve、create、update、destory等方法都需要自己編寫,而這些方法與前面講過的Mixin擴充套件類提供的方法同名,所以我們可以通過繼承Mixin擴充套件類來複用這些方法而無需自己編寫。但是Mixin擴充套件類依賴與GenericAPIView,所以還需要繼承GenericAPIView

GenericViewSet就幫助我們完成了這樣的繼承工作,繼承自GenericAPIViewViewSetMixin,在實現了呼叫as_view()時傳入字典(如{'get':'list'})的對映處理工作的同時,還提供了GenericAPIView提供的基礎方法,可以直接搭配Mixin擴充套件類使用。

舉例:

from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin
class Student4ViewSet(GenericViewSet,ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

url的定義

urlpatterns = [
    path("students7/", views.Student4ViewSet.as_view({"get": "list", "post": "create"})),
    re_path("students7/(?P<pk>\d+)/", views.Student4ViewSet.as_view({"get": "retrieve","put":"update","delete":"destroy"})),

]

3)ModelViewSet

繼承自GenericViewSet,同時包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。

# 使用ModelViewSet寫5個api,url需要修改
# ModelViewSet繼承了5中擴充套件檢視類,所以只要配置好就可以使用5個對應的api方法
from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet,ViewSetMixin
class Students4API(ModelViewSet):
    queryset = models.Student.objects
    serializer_class = ser.StudentModelSerializer
    
# urls
 url(r'^student4/$',views.Students4API.as_view(actions={
        'get':'list','post':'create'
    })),
    url(r'^student4/(?P<pk>\d+)',views.Students4API.as_view(actions={
        'get':'retrieve','put':'update','delete':'destroy'
    })),
    

4)ReadOnlyModelViewSet

繼承自GenericViewSet,同時包括了ListModelMixin、RetrieveModelMixin。

# ReadOnlyModelViewSet對應get全部和get單個
class Student5API(ReadOnlyModelViewSet):
    queryset = models.Student.objects
    serializer_class = ser.StudentModelSerializer
    
# urls
url(r'^student5/$',views.Student5API.as_view(actions={'get':'list'})),
 url(r'^student5/(?P<pk>\d+)',views.Student5API.as_view(actions={'get':'retrieve'})),

四、檢視類繼承關係

檢視擴充套件類Mixin都是頂級繼承

ListModelMixin,CreateModelMixin,UpdateModelMixin,DestroyModelMixin,RetrieveModelMixin 都繼承自object

ViewSetMixin 它是檢視集的頂級父類,所有檢視集最終都繼承它

GenericAPIView檢視子類都繼承它,也繼承對應關係的檢視擴充套件Mixin類

CreateAPIView,ListAPIView,UpdateAPIView,RetrieveAPIView,DestroyAPIView,ListCreateAPIView,RetrieveUpdateDestroyAPIView,RetrieveDestroyAPIView,RetrieveUpdateAPIView

通用檢視子類繼承關係

檢視基類繼承關係

GenericAPIView----》APIView----》View

檢視集繼承關係

xxx檢視集繼承關係查詢方式:

我們可以先看名字,如果結尾是ViewSet,可以確定是一個檢視集

普通檢視集---》GenericViewSet----》ViewSetMixin

普通檢視集也會繼承對應的Mixin擴充套件類

比如ReadOnlyModelViewSet,是隻讀檢視集,就是隻有get全部和get單個的方法,那它必定還繼承ListModelMixin和RetrieveModelMixin擴充套件類

總結:繼承關係只要知道繼承的方式就很好判斷,所以要明白什麼是檢視集,什麼是基類檢視,什麼是擴充套件類,通用檢視和通用檢視子類