目錄
一、DRF的視圖家族
1. DRF中視圖家族成員和配套路由
(1)視圖類:views
-
# 包括:APIView,GenericAPIView(generics中)兩個視圖類 APIView類: 1. 繼承View,所以擁有View的所有 2. 重寫as_view方法 3. 重寫dispatch方法 4. 一系列類屬性 GenericAPIView類: 1. 繼承APIView,所以擁有APIView的所有 2. 有get_queryset方法,來配置queryset類屬性,提供了要處理的資源對象列表 3. 在第二條基礎上,通過get_object方法,提供了處理的具體資源對象。配置lookup_url_kwarg類屬性是因為配置的url中傳的有名分組的名字可能不是pk,這使就可以通過lookup_url_kwarg來替代pk 4. get_serializer方法,配置serializer_class類屬性,提供視圖類相關的序列化對象 ***配置的queryset屬性和serializer_class屬性為視圖工具准備數據*** (GenericAPIView類中: queryset = None serializer_class = None lookup_field = 'pk' lookup_url_kwarg = None ) 總結:GenericAPIView就是在APIView基礎上額外提供了三個方法(get_queryset,get_object,get_serializer),三個類屬性(queryset,lookup_url_kwarg,serializer_class),如果不配合視圖工具類,體現不出優勢。 GenericAPIView內部調用視圖工具中的6種操作資源的對應實現體方法 目的:視圖中的增刪改查邏輯相似,但操作的資源不一致。操作資源就是操作:資源對象、資源對象們 以及 資源相關的序列化類,將這三者形成類屬性的配置,由程序員自己從外部根據不同資源傳入不同類屬性,這樣一來,不同資源的操作邏輯就都一致了,就可以進行封裝
(2)視圖工具類:mixins
-
視圖工具類的作用就是完成對資源的操作,並返回數據的序列化結果給前端
-
視圖工具含有的6種類的實現體方法,調用這6個實現體方法的方法就是
GenericAPIView提供的,所以要配合GenericAPIView類使用 -
# 包括: CreateModelMixin # 單增工具 含有create方法,實現增邏輯和返回響應結果 RetrieveModelMixin # 單查工具 含有retrieve方法,實現單查邏輯和返回響應結果 ListModelMixin # 群查工具 含有list方法,實現群查邏輯和返回響應結果 UpdateModelMixin # 單改工具(包含整體單改和局部單改) 含有update方法和partial_update方法,分別實現整體單增和局部單增邏輯和返回響應結果 (update方法內部的含有partial參數,默認是False。partial_update方法其實就是將partial參數置為True,再用update方法) DestroyModelMixin # 單刪工具 含有destroy方法,實現單刪邏輯和返回響應結果
(3)工具視圖類:generics
-
工具視圖類是對視圖工具和視圖類兩者的繼承,內部包含了對資源的操作和處理后的響應
-
# 包括:以下等共九種工具視圖(其實就是對視圖工具和視圖的組合) CreateAPIView # 單增工具視圖 RetrieveAPIView # 單查工具視圖 ListAPIView # 群查工具視圖 RetrieveUpdateDestroyAPIView # 單查單改(包括整體改和局部改)單刪工具視圖
(4)視圖集:viewsets
-
實現:在一個請求方式只有一條url路由對應時,對應的單操作和群操作可以區分開。
-
視圖集的核心是繼承了
ViewSetMixin類 -
視圖集中的
-
1. ViewSetMixin類 ViewSetMixin類重寫了as_view方法,相比APIView的as_view方法,額外多出了一個參數actions 重寫的as_view方法可以傳字典形式的參數。通過傳入的參數將不同的請求方式對應到各自的處理函數 例子:as_view({'get': 'list'}) 傳入的{'get': 'list'}就被actions接收,原理是將get請求映射給視圖類的list函數進行處理 2. 兩個視圖集基類:GenericViewSet和ViewSet的區別 都繼承了ViewSetMixin類,只是繼承的視圖類不同 GenericViewSet(ViewSetMixin, GenericAPIView),該分支嚴格滿足資源接口規范 ViewSet(ViewSetMixin, APIView),該分支滿足的接口與資源Model類關系不是特別密切:登錄接口、短信驗證碼接口
-
(5)配套路由:SimpleRouter
- 與視圖家族對應的路由層
SimpleRouter - 實現對同資源的不同操作對應url的簡化,同資源只用書寫一個url
- 用法見下面:二的3中的代碼中的。
2. DRF的視圖家族的作用和注意點
(1)視圖家族的作用
- 為我們封裝了資源操作中的六大接口:
- 單查,群查
- 單增
- 整體單改
- 局部單改
- 單刪
(2)視圖家族的不足之處和注意點
-
封裝的6大接口內部的邏輯都已經完成了。但另外的4大接口沒有提供,需要我們自己書寫方法和邏輯
-
視圖家族封裝的6大接口的響應數據中只包含資源數據,沒有數據狀態碼和狀態信息,需要我們重寫響應方法
-
在6大接口中,刪除接口直接刪除的是資源本身,因此需要我們重寫單刪和群刪接口
-
在自己配置類屬性queryset時,最后要加上 all 方法 例子:models.Car.objects.filter(is_delete=False).all()
3. 視圖家族的使用方法
- 共有4種使用方式
1. 直接使用視圖類APIView,自己書寫操作資源的十大接口和響應方法
2. 自定義視圖類GenericAPIView和視圖工具的組合再使用
3. 直接使用工具視圖類(其實和第二種方式類似,只是第二種是我們自己手動組合,自己聯合使用;第三種是工具視圖類已經分別組合好了的5種獨立接口和4個組合接口,其中update獨立接口包含整體改和局部改)
4. 使用視圖集中的ModelViewSet類,該類中包含6大接口(實際開發中最常用的使用方式)
# *******************使用2,3,4方式時的相同點:(重點)********************
他們都必須要配置queryset類屬性和serializer_class類屬性,有時也要配置lookup_url_kwarg類屬性。
二、視圖家族的使用實例
1. 自定義視圖類GenericAPIView和視圖工具的組合
from rest_framework.mixins import RetrieveModelMixin, ListModelMixin, CreateModelMixin
from rest_framework.generics import GenericAPIView
class CarReadCreateGenericAPIView(ListModelMixin, RetrieveModelMixin, CreateModelMixin, GenericAPIView):
queryset = models.Car.objects.filter(is_delete=False).all()
serializer_class = serializers.CarModelSerializer
lookup_url_kwarg = 'pk'
# 群查
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
# 單增
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
2. 直接使用工具視圖類
# 獨立單查接口
from rest_framework.generics import RetrieveAPIView
class CarRetrieveAPIView(RetrieveAPIView):
queryset = models.Car.objects.filter(is_delete=False).all()
serializer_class = serializers.CarModelSerializer
lookup_url_kwarg = 'pk'
# 獨立群查接口
from rest_framework.generics import ListAPIView
class CarListAPIView(ListAPIView):
queryset = models.Car.objects.filter(is_delete=False).all()
serializer_class = serializers.CarModelSerializer
# 組合接口:單查(get)、單整體改(put)、單局部改(patch)、單刪接口(delete)
from rest_framework.generics import RetrieveUpdateDestroyAPIView
class CarRetrieveUpdateDestroyAPIView(RetrieveUpdateDestroyAPIView):
queryset = models.Car.objects.filter(is_delete=False).all()
serializer_class = serializers.CarModelSerializer
3. 使用視圖集
# 組合接口的使用
from rest_framework.viewsets import ViewSetMixin, GenericViewSet, ViewSet, ModelViewSet
# 完成資源操作的6大接口
class CarModelViewSet(ModelViewSet):
queryset = models.Car.objects.filter(is_delete=False).all()
serializer_class = serializers.CarModelSerializer
# 視圖集的urls文件中:
# 寫法一:
from django.conf.urls import url, include
from . import views
urlpatterns = [
url(r'^cars/$', views.CarModelViewSet.as_view({
'get': 'list',
'post': 'create',
'put': 'many_update',
'patch': 'many_partial_update',
'delete': 'many_destroy',
})),
url(r'^cars/(?P<pk>\d+)/$', views.CarModelViewSet.as_view({
'get': 'retrieve',
'put': 'update',
'patch': 'partial_update',
'delete': 'destroy',
})),
]
# 寫法二:
# 使用與視圖家族對應的路由類SimpleRouter來寫路由
# 路由層:外面會遇到這種寫法,看到了要認識
from django.conf.urls import url, include
from . import views
from rest_framework.routers import SimpleRouter
router = SimpleRouter()
router.register('v7/cars', views.CarModelViewSet, basename='car') # cars后面沒有 /
urlpatterns = [
url(r'', include(router.urls))
]
三、視圖家族的使用優化
- 因為視圖家族有他的不足之處,如下
# 分析:從實際開發角度分析不合理點
1)沒有群增,群整體改,群局部改,群刪四個接口
2)刪除操作視圖集默認走的destroy方法是將資源從數據庫中刪除,實際通常刪除時通常是對字段is_delete做修改,表示刪除
3)響應的結果只有數據,沒有數據狀態碼和狀態信息
- 解決方法
# 自定義響應模塊(包含數據狀態碼,狀態信息和數據)
from rest_framework.response import Response
class APIResponse(Response):
def __init__(self, status=0, msg='ok', results=None, http_status=None,
headers=None, exception=False, content_type=None, **kwargs):
# 將status、msg、results、kwargs格式化成data
data = {
'status': status,
'msg': msg,
}
# results只要不為空都是數據:False、0、'' 都是數據 => 條件不能寫if results
if results is not None:
data['results'] = results
# 將kwargs中額外的k-v數據添加到data中
data.update(**kwargs)
super().__init__(data=data, status=http_status, headers=headers, exception=exception, content_type=content_type)
解決1(自己寫4個群操作接口)
# 群整體改,群局部改,全刪三個接口可以獨立成三個方法
def many_update(self, request, *args, **kwargs):
return APIResponse(msg='這個地方是群整體改,你會寫!')
def many_partial_update(self, request, *args, **kwargs):
return APIResponse(msg='這個地方是群局部改,你會寫!')
def many_destroy(self, request, *args, **kwargs):
return APIResponse(msg='這個地方是群刪,你會寫!')
# 群增與單增必須公用一個接口,都要走create方法,所以重寫create方法,用邏輯進行拆分
def create(self, request, *args, **kwargs):
request_data = request.data
if isinstance(request_data, list):
car_ser = self.get_serializer(data=request_data, many=True)
car_ser.is_valid(raise_exception=True)
car_obj = car_ser.save()
return APIResponse(msg='群增成功', results=self.get_serializer(car_obj, many=True).data)
return super().create(request, *args, **kwargs)
# 解決2(重寫單刪的destroy方法,自定義實現體)
def destroy(self, request, *args, **kwargs):
car_obj = self.get_object()
car_obj.is_delete = True
car_obj.save()
return APIResponse(msg='刪除成功')
# 解決3(讓群查有狀態碼和狀態信息 - 例如重寫list方法)
def list(self, request, *args, **kwargs):
response = super().list(request, *args, **kwargs)
return APIResponse(results=response.data)
