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 只是用于提交数据,返回值也不是登录用户信息,而是登录的认证信息。

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