django rest framework中對於APIView、GenericAPIView、ModelViewSet、mixins擴展類的分析。python
根據實際程序來分析:數據庫
urls.py
django
urlpatterns = [ re_path('users', UserAPIView.as_view()) ]
views.py
api
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
。iview
原生
View
裏面定義了容許的http訪問方式。函數![]()
urls.py
中,UserAPIView.as_view()
這條語句其實執行的是APIView
中的as_view()
方法,而APIView
中的as_view()
方法執行父類View
的as_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:
as_view()
是入口,獲得view函數view
函數,內部調用dispatch
函數完成請求分發dispatch
函數將請求方式映射爲視圖類的同名方法,獲得相應結果APIView:
as_view()
是入口,經過執行父類中的as_view
方法獲得view函數,而後在返回view
函數的時候免除csrf驗證。view
函數,內部調用(APIView類中的)dispatch
方法完成請求分發。dispatch
方法中,將會二次封裝request,完成三大驗證(認證、受權、節流),再將請求方式映射爲視圖類的同名方法,完成請求的處理。首先看看GenericAPIView的源碼。
能夠看出,GenericAPIView
繼承了APIView
。而後還有幾個比較重要的類屬性,稍後用到再講。
再往下看它對外暴露的方法:
實際上是比較少的,並且大可能是get_xxx
之類的方法,也就是說,它的as_view()
、dispatch
等方法,其實都是按照APIView
的流程處理的。
首先咱們看get_queryset()
,它的意思是批量查找數據庫的數據:
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_field
爲name
。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)
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)
若是繼承全部的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擴展類,提供瞭如下幾個功能:
類 | 功能 |
---|---|
CreateAPIView | 建立記錄 |
DestroyAPIView | 刪除記錄 |
UpdateAPIView | 更新單條記錄 |
ListAPIView | 查詢全部記錄 |
RetrieveAPIView | 查詢單條記錄 |
ListCreateAPIView | 建立以及查詢全部記錄 |
RetrieveUpdateAPIView | 更新以及查詢單條記錄 |
RetrieveDestroyAPIView | 刪除以及查詢單條記錄 |
RetrieveUpdateDestroyAPIView | 刪除、更新、查詢單條記錄 |
因而可知,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放在前面。
看完上面的內容,下面再看這張圖片,但願能夠幫助梳理本篇文章的內容: