前言
ViewSet
只是一種基於類的檢視,它不提供任何方法處理程式(如 .get()
或.post()
),而是提供諸如.list()
和 .create()
之類的操作。
ViewSet
的方法處理程式僅使用 .as_view()
方法繫結到完成檢視的相應操作。
通常不是在urlconf
中的檢視集中顯示註冊檢視,而是要使用路由類註冊檢視集,該類會自動為你確定 urlconf
。
原始碼分析
我們首先看一下viewsets.py
檔案的原始碼結構,如下圖
我們可以看到有5個類
- ViewSetMixin
- ViewSet:繼承自
ViewSetMixin
和APIView
- GenericViewSet:繼承自
ViewSetMixin
和GenericAPIView
- ReadOnlyModelViewSet:繼承自
RetrieveModelMixin
、ListModelMixin
、GenericViewSet
- 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
繼承了ViewSetMixin
和APIView
,增刪改查需要我們自己定義
GenericViewSet
class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
"""
GenericViewSet 類預設不提供任何操作,但包含通用檢視行為的基本集,例如`get_object` 和`get_queryset` 方法。
"""
pass
GenericViewSet
類相比ViewSet
,包含了一些檢視行為的通用方法
檢視集特點
- 檢視集都是優先繼承
ViewSetMixin
類,再繼承一個檢視類(GenericAPIView或APIView)
ViewSetMixin
提供了重寫的as_view()
方法,繼承檢視集的檢視類,配置路由時呼叫as_view()
必須傳入 請求-函式名 對映關係字典
eg: path('v1/books/<int:pk>/', views.BookGenericViewSet.as_view({"get": "my_get_obj"}))
GenericAPIView與APIView 作為兩大繼承檢視的區別
GenericViewSet
和ViewSet
都繼承了ViewSetMixin
,as_view
都可以配置 請求-函式 對映GenericViewSet
繼承的是GenericAPIView
檢視類,用來完成標準的model
類操作介面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
。