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()
方法執行父類View
的as_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:
as_view()
是入口,得到view函式- 請求來了呼叫
view
函式,內部呼叫dispatch
函式完成請求分發 dispatch
函式將請求方式對映為檢視類的同名方法,得到相應結果
APIView:
as_view()
是入口,通過執行父類中的as_view
方法得到view函式,然後在返回view
函式的時候免除csrf驗證。- 請求來了呼叫
view
函式,內部呼叫(APIView類中的)dispatch
方法完成請求分發。 - 在
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_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)
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 | 刪除、更新、查詢單條記錄 |
由此可見,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放在前面。
看完上面的內容,下面再看這張圖片,希望可以幫助梳理本篇文章的內容: