前言

ViewSet 只是一種基於類的檢視,它不提供任何方法處理程式(如 .get().post()),而是提供諸如.list().create() 之類的操作。

ViewSet 的方法處理程式僅使用 .as_view() 方法繫結到完成檢視的相應操作。

通常不是在urlconf中的檢視集中顯示註冊檢視,而是要使用路由類註冊檢視集,該類會自動為你確定 urlconf

原始碼分析

我們首先看一下viewsets.py檔案的原始碼結構,如下圖



我們可以看到有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