DRF視圖家族


一、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)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM