Django REST 框架詳解 06 | 視圖家族 Generics 與 Viewsets

一、Generics:工具視圖

generics 是工具視圖,可以實現極簡化接口編寫操作。

工具視圖都是 GenericAPIView 的子類,不同的子類繼承不同工具類,重寫請求方法。
mark

1.羣查與單增:ListCreateAPIView

查看源碼
# 繼承了視圖基類 GenericAPIView,工具類 ListModelMixin,CreateModelMixin實現羣查和單增
class ListCreateAPIView(mixins.ListModelMixin,
                        mixins.CreateModelMixin,
                        GenericAPIView):
    """
    Concrete view for listing a queryset or creating a model instance.
    """
    # 羣查
    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)
	
    # 單增
    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)
代碼實現

urls.py

from django.conf.urls import url, include

from api import views

urlpatterns = [
	url(r'^v4/books/$', views.BookListCreateView.as_view()),
    url(r'^v4/books/(?P<pk>.*)/$', views.BookListCreateView.as_view()),
]

工具視圖的功能如果滿足需求,只需要繼承工具視圖,添加 queryset,serializer_class

views.py

class BookListCreateView(ListCreateAPIView):
    queryset = models.Book.objects.filter(is_delete=False)
    serializer_class = serializers.BookModelSerializer

測試接口

羣查
mark

單增
mark

入庫成功
mark

其他方法

根據上述源碼分析與示例和下面的圖示,我們可以很容易知道這些類的功能和用法
mark

2.添加其他接口

代碼實現
# 需要什麼接口,直接繼承就行
# 比如我們在羣查,單增的基礎上,添加單改接口
class BookListCreateView(ListCreateAPIView, UpdateAPIView):
    queryset = models.Book.objects.filter(is_delete=False)
    serializer_class = serializers.BookModelSerializer
接口測試

mark

修改成功mark

3.後續問題

但是上述雖然可以實現簡單接口,但是有時候需求會很複雜。

比如,來自前端用戶的數據格式並不是和我們規定的一樣,有可能傳來空值,錯誤字符等等。這就需要對 request.data 進行過濾,尤其是在入庫的時候。

另外,如果數據有誤,DRF 並不知道你的字段是哪出的問題,所以拋異常是隻會是數據錯誤。我們需要對每個字段的每種錯誤類型給出對應的返回值。

可以在 UpdateModelMixin 源碼中看到,request.data 並沒有進行過濾。

class UpdateModelMixin:
   
    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        # request.data 並沒有進行過濾
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        # ...

二、Viewsets:視圖集

1.簡單使用

DRF 提供了 Viewsets.py 視圖集,再次封裝之前的操作。最主要的是,可以通過設置 請求-函數 映射關係,來將請求方式與原有方法或自定義方法對應執行。

查看源碼

mark

發現沒有提供實際的方法

class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
    """
    The GenericViewSet class does not provide any actions by default,
    but does include the base set of generic view behavior, such as
    the `get_object` and `get_queryset` methods.
    """
    pass

但是看到它繼承了 ViewSetMixin,GenericAPIView

查看 ViewSetMixin 類的 as_view 方法

GenericViewSet 和 ViewSet 都繼承了 ViewSetMixin,as_view 可以配置 請求-函數 映射

比如view = MyViewSet.as_view({'get': 'list', 'post': 'create'})

class ViewSetMixin:

    @classonlymethod
    def as_view(cls, actions=None, **initkwargs):
        #...

        def view(request, *args, **kwargs):
            # 這裏 cls 去解析前邊例子中的 {'get': 'list', 'post': 'create'}
            self = cls(**initkwargs)
            self.action_map = actions

            # methods拿到請求方法,比如get
            for method, action in actions.items():
                handler = getattr(self, action)
                # 映射method get到執行函數handler list
                setattr(self, method, handler)

            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get

            self.request = request
            self.args = args
            self.kwargs = kwargs

            # 繼承 APIView 中的 dispatch 進行分發
            return self.dispatch(request, *args, **kwargs)
		
        # ...
代碼實現

這樣的好處是,各種需求的接口的請求方式都可以用不同函數定義返回值。比如十大接口對應十個函數,分別碼代碼。

views.py

class BookGenericViewSet(RetrieveModelMixin, ListModelMixin, GenericViewSet):
    queryset = models.Book.objects.filter(is_delete=False)
    serializer_class = serializers.BookModelSerializer

    def get_list(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def get_obj(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

urls.py

from django.contrib import admin
from django.urls import path
from django.conf.urls import url, include
from django.views.static import serve
from django.conf import settings

from api import views

urlpatterns = [
    # ...
    url(r'^v5/books/$', views.BookGenericViewSet.as_view({'get':'get_list'})),
    url(r'^v5/books/(?P<pk>.*)/$', views.BookGenericViewSet.as_view({'get':'get_obj'})),
]

2.ModelViewSet:最全的封裝類

查看源碼
class ModelViewSet(mixins.CreateModelMixin,
                   mixins.RetrieveModelMixin,
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet):
    """
    A viewset that provides default `create()`, `retrieve()`, `update()`,
    `partial_update()`, `destroy()` and `list()` actions.
    """
    pass
代碼實現

views.py

# 繼承 ModelViewset 會直接擁有六大接口:單查,羣查,單增,單刪,單整體改,單局部改
# 需要注意:Destroy 需要重寫
class BookModelViewSet(ModelViewSet):
    queryset = models.Book.objects.filter(is_delete=False)
    serializer_class = serializers.BookModelSerializer

    # 刪除操作
    def destroy(self, request, *args, **kwargs):
        instance = self.get_object() # type: models.Book
        if not instance:
            return APIResponse(1, "Delete fail")
        instance.is_delete = True
        instance.save()
        return APIResponse(1, "Delete successful")

urls.py

from django.conf.urls import url

from api import views

urlpatterns = [
    url(r'^v6/books/$', views.BookModelViewSet.as_view({'get':'list', 'post':'create'})),
    url(r'^v6/books/(?P<pk>.*)/$', views.BookModelViewSet.as_view({'get': 'retrieve', 'post':'create', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'})),
]

接口測試
羣查

mark

單查

mark

單刪

mark
查看數據庫
mark

單增

mark

入庫成功mark

等等

總結

GenericAPIView 與 APIView 的區別與適用場景

1. GenericAPIView 視圖類

GenericView 繼承 GenericAPIView 視圖類

適用於標準的接口請求,或實現標準的 Model 類操作接口。

案例: 用戶查詢時,發送 GET 請求,返回數據。

2. APIView 視圖類

ViewSet 繼承 APIView 視圖類

實現不需要 Model 類操作,或非標準的 Model 類操作接口。比如,POST請求在標準的 Model 類操作用於新增接口,但以下案例並不符合這個標準。

案例 1: 請求手機驗證碼時,發送 POST 請求,不需要 Model 類的參與。

案例 2: 用戶登錄時,發送的 POST 請求,並不是完成數據的新增。POST 只是用於提交數據,返回值也不是登錄用戶信息,而是登錄的認證信息。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章