DRF檢視的使用及原始碼流程分析

wushengCN發表於2020-12-29

django rest framework中對於APIView、GenericAPIView、ModelViewSet、mixins擴充套件類的分析。

APIView

示例

根據實際程式來分析:

urls.py

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

views.py

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繼承了View

原生View裡面定義了允許的http訪問方式。

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

APIView中的as_view()

View中的as_view()

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

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使用有奇效。
  • 會自動進行return Response(),所以就不用我們再對返回物件進行包裝了。
方法 描述
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放在前面。

看完上面的內容,下面再看這張圖片,希望可以幫助梳理本篇文章的內容:

相關文章