DRF視圖的使用及源碼流程分析

django rest framework中對於APIView、GenericAPIView、ModelViewSet、mixins擴展類的分析。python

APIView

示例

根據實際程序來分析:數據庫

urls.pydjango

urlpatterns = [
    re_path('users', UserAPIView.as_view())
]

views.pyapi

class UserAPIView(APIView):
  
    def get(self, request):
        users = User.objects.filter().all()
        ser = UserSerializer(instance=users, many=True)
        return CommonResponse(status=status.HTTP_200_OK, data=ser.data)

源碼分析

首先,UserAPIView繼承了APIView,而APIView繼承了Viewiview

原生View裏面定義了容許的http訪問方式。函數

urls.py中,UserAPIView.as_view()這條語句其實執行的是APIView中的as_view()方法,而APIView中的as_view()方法執行父類Viewas_view()方法。源碼分析

APIView中的as_view()url

View中的as_view()spa

其中關鍵的方法爲dispatch方法,根據UserAPIView-->APIView-->View的繼承順序,該方法執行的是APIView中的dispatch方法。看下圖。rest

dispatch方法會獲取請求方式,判斷是不是http容許的請求方式,若是是的話,則分發執行UserAPIView中對應的同名方法。

其實咱們也能夠在UserAPIView中自定義dispatch方法,以下:

def dispatch(self, request, *args, **kwargs):
    func = getattr(self,request.method.lower())
    return func(request,*args,**kwargs)

總結:CBV基於反射實現根據請求方式不一樣,執行不一樣的方法。

View與APIView的執行流程

View:

  1. as_view()是入口,獲得view函數
  2. 請求來了調用view函數,內部調用dispatch函數完成請求分發
  3. dispatch函數將請求方式映射爲視圖類的同名方法,獲得相應結果

APIView:

  1. as_view()是入口,經過執行父類中的as_view方法獲得view函數,而後在返回view函數的時候免除csrf驗證。
  2. 請求來了調用view函數,內部調用(APIView類中的)dispatch方法完成請求分發。
  3. dispatch方法中,將會二次封裝request,完成三大驗證(認證、受權、節流),再將請求方式映射爲視圖類的同名方法,完成請求的處理。

GenericAPIView

執行流程

首先看看GenericAPIView的源碼。

能夠看出,GenericAPIView繼承了APIView。而後還有幾個比較重要的類屬性,稍後用到再講。

再往下看它對外暴露的方法:

實際上是比較少的,並且大可能是get_xxx之類的方法,也就是說,它的as_view()dispatch等方法,其實都是按照APIView的流程處理的。

重要方法

首先咱們看get_queryset(),它的意思是批量查找數據庫的數據:

  • 能夠看到,queryset屬性必須進行賦值,按照繼承順序,咱們直接在本身定義的視圖類中聲明queryset屬性並進行賦值。
  • 在if判斷中,若是queryset是一個QuerySet對象,就獲取所有,獲得一個QuerySetDict對象,不然原樣返回。

QuerySetDict類型以下:

接下來看get_object(),它的意思是從數據庫中獲取單個對象:

  • filter_kwargs封裝查找條件,get_object_or_404負責在queryset中查找符合條件的對象(object)。
  • 看到這裏就發現了,默認查詢條件是用pk,也就是說你的url中必需要用pk這個形參名進行分組捕獲。不然就須要聲明lookup_url_kwarg,若是url中的參數爲name,那麼lokup_url_kwarg="name",而後進行替換組建filter_kwargs。固然若是你的查詢條件不是用的pk,就須要修改lookup_field爲字段名,如我不是按照pk進行查詢,而是按照name,就修改lookup_fieldname
  • 同時也會檢查用戶是否有權限查看該對象。check_object_permissions

主要的就是以上兩個,get_serializer()會調用get_serializer_class獲取序列化類,返回序列化類的執行結果。

示例

class UserAPIView(GenericAPIView):
    queryset = User.objects
    serializer_class = UserSerializer

    def get(self, request):
        users = self.get_queryset()
        ser = self.get_serializer(instance=users, many=True)
        return CommonResponse(status=status.HTTP_200_OK, data=ser.data)

mixins擴展類

  • rest_framework.mixins中,有五個擴展類,分別是:
    • ListModelMixin:該類主要負責查詢全部記錄
    • RetrieveModelMixin:該類主要負責查詢單條記錄
    • CreateModelMixin:該類主要負責建立記錄
    • UpdateModelMixin:該類主要負責對記錄作更新操做
    • DestroyModelMixin:該類主要負責刪除記錄
  • 這五個類都繼承於object,是獨立的子類。
  • 針對GenericAPIView更高級的封裝,配合GenericAPIView使用有奇效。
方法 描述
ListModelMixin list() 查詢全部,並返回Response對象
RetrieveModelMixin retrieve() 查詢單條,並返回Response對象
CreateModelMixin create() 建立記錄,並返回Response對象
UpdateModelMixin update() 更新記錄,並返回Response對象
DestroyModelMixin destroy() 刪除記錄,並返回Response對象

示例

class UserAPIView(GenericAPIView, ListModelMixin):
    queryset = User.objects
    serializer_class = UserSerializer

    def get(self, request):
        return CommonResponse(status=status.HTTP_200_OK, data=self.list(request).data)

ModelViewSet

若是繼承全部的mixins類,則視圖類就會寫的很是冗長,可讀性較差。

同時,每次都要在視圖中return,那咱們會考慮能不能簡化這步操做。

首先導入ModelViewSet

from rest_framework.viewsets import ModelViewSet

查看它的源碼:

再看GenericViewSet

再看ViewSetMixin

註釋的意思是說,它重寫了as_view()方法,綁定了http方法與視圖函數中的方法,這讓它必須接收一個actions參數,actions參數設置爲一個字典。

示例

urls.py

urlpatterns = [
    re_path('users', UserAPIView.as_view(actions={"get": 'list'}))
]

views.py

class UserAPIView(ModelViewSet):
    queryset = User.objects
    serializer_class = UserSerializer

generics擴展類

至於generics擴展類,提供瞭如下幾個功能:

功能
CreateAPIView 建立記錄
DestroyAPIView 刪除記錄
UpdateAPIView 更新單條記錄
ListAPIView 查詢全部記錄
RetrieveAPIView 查詢單條記錄
ListCreateAPIView 建立以及查詢全部記錄
RetrieveUpdateAPIView 更新以及查詢單條記錄
RetrieveDestroyAPIView 刪除以及查詢單條記錄
RetrieveUpdateDestroyAPIView 刪除、更新、查詢單條記錄
他們的實現也很簡單,以`RetrieveDestroyAPIView`類舉例:

因而可知,generics中的擴展類都是結合了mixins擴展類GenericAPIView,而且提供了標準的http方法同名方法,這些方法又都分別執行對應的mixins擴展類中的方法。這和咱們mixins擴展類部分中的示例大致一致。

示例

urls.py

urlpatterns = [
    re_path('users', UserAPIView.as_view(actions={"get": 'list'}))
]

Views.py

class UserAPIView(ViewSetMixin, ListAPIView):
    queryset = User.objects
    serializer_class = UserSerializer

注:UserAPIView的as_view方法要接收actions參數,而咱們要使用的就是ViewSetMixin中重寫的as_view方法,所以爲了避免和ListAPIView中的as_view產生衝突,繼承時要把ViewSetMixin放在前面。

看完上面的內容,下面再看這張圖片,但願能夠幫助梳理本篇文章的內容:

相關文章
相關標籤/搜索