Django(58)viewsets檢視集詳解

Silent丿丶黑羽 發表於 2021-06-10

前言

ViewSet 只是一種基於類的檢視,它不提供任何方法處理程式(如 .get().post()),而是提供諸如.list().create() 之類的操作。
ViewSet 的方法處理程式僅使用 .as_view() 方法繫結到完成檢視的相應操作。
通常不是在urlconf中的檢視集中顯示註冊檢視,而是要使用路由類註冊檢視集,該類會自動為你確定 urlconf
 

原始碼分析

我們首先看一下viewsets.py檔案的原始碼結構,如下圖
Django(58)viewsets檢視集詳解
我們可以看到有5個類

  • ViewSetMixin
  • ViewSet:繼承自ViewSetMixinAPIView
  • GenericViewSet:繼承自ViewSetMixinGenericAPIView
  • ReadOnlyModelViewSet:繼承自RetrieveModelMixinListModelMixinGenericViewSet
  • ModelViewSet:繼承自5大mixins工具類和GenericViewSet
     

ViewSetMixin

通過上述程式碼結構分析,我們瞭解到只要知道ViewSetMixin是幹嘛的,其他的類都繼承於它。從原始碼中我們知道,ViewSetMixin重寫了as_view()方法,原始碼如下:

def as_view(cls, actions=None, **initkwargs):
    """
    由於基於類的檢視圍繞例項化檢視建立閉包的方式,我們需要完全重新實現`.as_view`,並稍微修改建立和返回的檢視函式。 
    對於某些路由配置,initkwargs 的名稱和描述可能會被明確覆蓋,例如,額外操作的名稱。
    """
    # 名稱和描述 initkwargs 可能會被顯式覆蓋
    cls.name = None
    cls.description = None
    
    # 字尾 initkwarg 保留用於顯示檢視集型別。如果提供了名稱,則此 initkwarg 應該無效。
    cls.suffix = None

    cls.detail = None
    
    # 設定 basename 允許檢視反轉其操作 url。該值由路由器通過 initkwargs 提供。
    cls.basename = None
    
    # actions必須不能為空,否則報錯
    if not actions:
        raise TypeError("The `actions` argument must be provided when "
                        "calling `.as_view()` on a ViewSet. For example "
                        "`.as_view({'get': 'list'})`")

    # 清理關鍵字引數
    for key in initkwargs:
        if key in cls.http_method_names:
            raise TypeError("You tried to pass in the %s method name as a "
                            "keyword argument to %s(). Don't do that."
                            % (key, cls.__name__))
        if not hasattr(cls, key):
            raise TypeError("%s() received an invalid keyword %r" % (
                cls.__name__, key))
    
    # name和suffix是互斥的
    if 'name' in initkwargs and 'suffix' in initkwargs:
        raise TypeError("%s() received both `name` and `suffix`, which are "
                        "mutually exclusive arguments." % (cls.__name__))
    
    def view(request, *args, **kwargs):
        self = cls(**initkwargs)

        if 'get' in actions and 'head' not in actions:
            actions['head'] = actions['get']

        self.action_map = actions
        
        # 將方法繫結到actions, 這是與標準檢視不同的一點
        for method, action in actions.items():
            handler = getattr(self, action)
            setattr(self, method, handler)

        self.request = request
        self.args = args
        self.kwargs = kwargs

        return self.dispatch(request, *args, **kwargs)

    update_wrapper(view, cls, updated=())

    update_wrapper(view, cls.dispatch, assigned=())

    view.cls = cls
    view.initkwargs = initkwargs
    view.actions = actions
    return csrf_exempt(view)

從上述原始碼中瞭解到,ViewSetMixin重寫了as_view方法,as_view是將請求的方法繫結到了actions
 

ViewSet

class ViewSet(ViewSetMixin, views.APIView):
    """
    預設情況下,基本 ViewSet 類不提供任何操作。
    """
    pass

ViewSet繼承了ViewSetMixinAPIView,增刪改查需要我們自己定義
 

GenericViewSet

class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
    """
    GenericViewSet 類預設不提供任何操作,但包含通用檢視行為的基本集,例如`get_object` 和`get_queryset` 方法。
    """
    pass

GenericViewSet類相比ViewSet,包含了一些檢視行為的通用方法
 

檢視集特點

  1. 檢視集都是優先繼承ViewSetMixin類,再繼承一個檢視類(GenericAPIView或APIView)
  2. ViewSetMixin提供了重寫的as_view()方法,繼承檢視集的檢視類,配置路由時呼叫as_view()必須傳入 請求-函式名 對映關係字典
    eg: path('v1/books/<int:pk>/', views.BookGenericViewSet.as_view({"get": "my_get_obj"}))
     

GenericAPIView與APIView 作為兩大繼承檢視的區別

  1. GenericViewSetViewSet都繼承了ViewSetMixinas_view都可以配置 請求-函式 對映
  2. GenericViewSet繼承的是GenericAPIView檢視類,用來完成標準的model類操作介面
  3. ViewSet繼承的是APIView檢視類,用來完成不需要model類參與,或是非標準的model類操作介面
    post請求在標準的model類操作下就是新增介面,登陸的post不滿足
    post請求驗證碼介面,不需要model類的參與
    案例:登陸的post請求,並不是完成資料的新增,只是用post提交資料,得到的結果也不是登陸的使用者資訊,而是登陸的認證資訊
     

ReadOnlyModelViewSet

class ReadOnlyModelViewSet(mixins.RetrieveModelMixin,
                           mixins.ListModelMixin,
                           GenericViewSet):
    """
    提供預設`list()` 和`retrieve()` 操作的檢視集。
    """
    pass

 

ModelViewSet

class ModelViewSet(mixins.CreateModelMixin,
                   mixins.RetrieveModelMixin,
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet):
    """
    一個提供預設 `create()`、`retrieve()`、`update()`、`partial_update()`、`destroy()` 和 `list()` 操作的檢視集。
    """
    pass

 

實戰案例

檢視函式如下

class StudentViewSets(viewsets.ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    def my_get(self, request, *args, **kwargs):
        response =  self.retrieve(request, *args, **kwargs)
        return APIResponse(results=response.data)

    def my_list(self, request, *args, **kwargs):
        response = self.list(request, *args, **kwargs)
        return APIResponse(results=response.data)

我們繼承自ModelViewSet,自帶5個mixins工具,我們定義了2個查詢方法,然後在urls中配置

urlpatterns = [
    path('v2/student/<int:pk>/', views.StudentViewSets.as_view({"get": "my_get"})),
    path('v2/student/', views.StudentViewSets.as_view({"get": "my_list"})),
]

as_view中新增了get請求方式的方法,有pk呼叫my_get代表單查,沒有pk呼叫my_list代表群查,這樣寫的原因就是我們的StudentViewSets繼承了ViewSetMixin