Contents

DRF 通用视图-视图工具类-视图集以及路由

:::tip Django REST Framework. Generic Views, MixIns, ViewSets and Routers :::

基本信息

源码仓库

rest_framework.views

rest_framework.generics

rest_framework.viewsets

rest_framework.routers

官方文档

API Guide-Generic views

API Guide-ViewSets

API Guide-Routers

关系图

::: details View继承图与View图谱 https://gitee.com/chen-zq/bgimages/raw/master/img/20201204_drf%E8%A7%86%E5%9B%BE%E9%9B%86%E5%9B%BE1.png https://gitee.com/chen-zq/bgimages/raw/master/img/20201204_drf%E8%A7%86%E5%9B%BE%E9%9B%86%E5%9B%BE2.jpg :::

AIPView-API视图类

APIView 是 Django REST Framework 提供的所有视图的基类,继承自 Django 的 View 父类

AIPView 对比 Django View

  1. 传入到视图方法中的是 REST framework 的 Request 对象,而不是 Django 的 HttpRequeset 对象
  2. 视图方法可以返回 REST framework 的 Response 对象,视图会为响应数据设置(render)符合前端要求的格式
  3. 任何 APIException 异常都会被捕获到,并且处理成合适的响应信息
  4. 在进行 dispatch() 分发前,会对请求进行身份认证、权限检查、流量控制

重要类属性

AIPView 有如下可设置的重要类属性:

  • authentication_classes:列表或元祖,身份认证类
  • permissoin_classes: 列表或元祖,权限检查类
  • throttle_classes: 列表或元祖,流量控制类

示例

views.py

from rest_framework.views import APIView
from rest_framework.response import Response

from models import Goods
from serializers import GoodsSerializer


class GoodListView(APIView):
    def get(self, request):
        goods = Goods.objects.all()
        serializer = GoodsSerializer(goods, many=True)
        return Response(serializer.data)

serializers.py

class GoodsSerializer(serializers.ModelSerializer):
    # category = CategorySerializer()
    # images = GoodsImageSerializer(many=True)
    class Meta:
        model = Goods
        fields = "__all__"

GenericAPIView-通用API视图类

通用 API 视图类 GenericAPIView 继承自 APIView,完全兼容 APIView,主要增加了操作序列化器和数据库查询的方法,作用是为下面 Mixin 扩展类的执行提供基础类支持。通常在使用时,可以配合一个或多个 Mixin 扩展类。

GenericAPIView 对比 APIView

  1. get_queryset(): 从类属性 queryset中获得 model 的 queryset 数据。群操作就走 get_queryset() 方法 (包括群查,群增等)。
  2. get_object(): 从类属性 queryset 中获得 model 的 queryset 数据,再通过有名分组 pk 确定唯一操作对象。单操作就走 get_object() 方法(包括单查,单增等)。
  3. get_serializer():从类属性 serializer_class 中获得 serializer 的序列化类。

重要类属性

  • 列表视图与详情视图共用
    • queryset: 指明视图需要的数据(model 查询数据)
    • permissoin_classes:指明视图使用的序列化器
  • 列表视图使用
    • pagination_class:指定分页控制类
    • filter_backends: 指定过滤控制后端
  • 详情页视图使用
    • lookup_field: 自定义主键,有名分组的查询,默认是 pk
    • lookup_url_kwarg:查询单一数据时 url 中的参数关键字名称,默认与 look_field 相同

重要类方法

  • get_queryset(): 从类属性 queryset 中获得 model 的 queryset 数据  
  • get_object(): 从类属性 queryset 中获得 model 的 queryset 数据,再通过有名分组 pk 来确定唯一操作对象
  • get_serializer():从类属性 serializer_class 中获得 serializer 的序列化类,主要用来提供给 Mixin 扩展类使用

get_serializer 源码

def get_serializer(self, *args, **kwargs):
    """
    Return the serializer instance that should be used for validating and
    deserializing input, and for serializing output.

    返回应该用于验证和反序列化输入以及序列化输出的序列化器实例。
    """
    serializer_class = self.get_serializer_class()
    kwargs['context'] = self.get_serializer_context()
    return serializer_class(*args, **kwargs)

示例

views.py

class GoodGenericAPIView(GenericAPIView):
    queryset = models.Good.objects.filter(is_delete=False)
    serializer_class = serializers.GoodModelSerializer
    lookup_field = 'pk'  # 先定义好,单查可以使用,默认是pk  自定义主键的有名分组,如果路由有名分组不是pk,这个属性就要自己设置了
    
    # 群取
    def get(self, request, *args, **kwargs):
        good_query = self.get_queryset()  # 获取queryset数据(model查询数据)
        good_ser = self.get_serializer(good_query, many=True)
        good_data = good_ser.data
        return APIResponse(results=good_data)

    # # 单取
    # def get(self, request, *args, **kwargs):
    #     good_query = self.get_object()
    #     good_ser = self.get_serializer(good_query)
    #     good_data = good_ser.data
    #     return APIResponse(results=good_data)

urls.py

urlpatterns = [
    path('v2/goods/', views.GoodGenericAPIView.as_view()),
    path('v2/goods/<pk>/', views.GoodGenericAPIView.as_view()),
]

xxxModelMixin-视图类的模型工具集

作用

  • 提供了几种后端视图(对数据资源的增删改查)处理流程的实现,如果需要编写的视图属于这五种,则视图可以通过继承相应的扩展类来复用代码,减少自己编写的代码量。
  • mixins 有五个工具类文件,一共提供了五个工具类,六个工具方法:单查、群查、单增、单删、单整体改、单局部改

使用

  • 继承工具类可以简化请求函数的实现体,但是必须继承 GenericAPIView,需要 GenericAPIView 类提供序列化器与数据库查询的方法 (见上方 GenericAPIView 基类知识点)
  • 工具类的工具方法返回值都是 Response 类型对象,如果要格式化数据格式再返回给前台,可以通过 response.data 拿到工具方法返回的 Response 类型对象的响应数据

五大模型工具类

  • ListModelMixin 群查
    • 列表视图扩展类,提供 list 方法快速实现查询视图
    • 返回 200 状态码
    • 除了查询,该 list 方法会对数据进行过滤和分页
  • CreateModelMixin 单增
    • 创建视图扩展类,提供 create 方法快速创建资源的视图,成功返回 201 的状态码
    • 没有群增的方法,需要自己手动写
  • RetrieveModelMixin 单查
    • 详情视图扩展类,提供 retrieve 方法,可以快速实现返回一个存在的数据对象
  • UpdateModelMixin 更新 / 修改
    • 更新视图扩展类,提供 update 方法,可以快速实现更新一个存在的数据对象,同时也提供 partial_update 方法,可以实现局部更新
    • 只有单整体改和单局部改,没有群整体改和群局部改
  • DestoryModelMixin 删除
    • 删除视图扩展类,提供 destory 方法,可以快速实现删除一个存在数据对象
    • 一般不怎么用到,因为实际开发中并不会真的删除数据,而是修改是否可用的标记

示例

views.py

from rest_framework.mixins import ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin

class GoodMixinGenericAPIView(ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, GenericAPIView):
    # GenericAPIView提供的序列化器和查询的数据
    queryset = models.Good.objects.filter(is_delete=False)
    serializer_class = serializers.GoodModelSerializer

    # 单查和群查
    def get(self, request, *args, **kwargs):
        if 'pk' in kwargs:
            # 单查  RetrieveModelMixin方法
            response = self.retrieve(request, *args, **kwargs)
        else:
            # mixins提供的list方法的响应对象是Response,将该对象格式化为自定义的APIResponse
            response = self.list(request, *args, **kwargs)  # 群查 ListModelMixin
        # response的数据都存放在response.data中
        return APIResponse(results=response.data)

    # 单增
    def post(self, request, *args, **kwargs):
        response = self.create(request, *args, **kwargs)    # CreateModelMixin方法
        return APIResponse(results=response.data)

    # 单整体修改
    def put(self, request, *args, **kwargs):
        response = self.update(request, *args, **kwargs)    # UpdateModelMixin
        return APIResponse(results=response.data)

    # 单局部修改
    def patch(self, request, *args, **kwargs):
        response = self.partial_update(request, *args, **kwargs)
        return APIResponse(results=response.data)

urls.py

urlpatterns = [
    path('v3/goods/', views.GoodMixinGenericAPIView.as_view()),
    path('v3/goods/<pk>/', views.GoodMixinGenericAPIView.as_view()),
]

xxxAPIView-功能性子视图类

功能性子视图类继承了 GenericAPIView 和各种 Mixins 工具类

  1. 功能性视图类都是 GenericAPIView 的子类,且不同的子类继承了不同的工具类
  2. 功能性视图类的功能可以满足需求,只需要继承工具视图,并且提供 queryset 与 serializer_class 即可

各大功能子视图类

视图 作用 请求类型 父类
ListAPIView 查询多条数据 get GenericAPIView ListModelMixin
CreateAPIView 新增一条数据 post GenericAPIView CreateModelMixin
RetrieveAPIView 查询一条数据 get GenericAPIView RetrieveModelMixin
UpdateAPIView 修改一条数据 put patch GenericAPIView UpdateModelMixin
DestroyAPIView 删除一条数据 delete GenericAPIView DestroyModelMixin
RetrieveUpdateAPIView 单查 更新一条 get put patch GenericAPIView RetrieveModelMixin UpdateModelMixin
RetrieveUpdateDestroyAPIView 单查 更新删除一条 get put patch delete GenericAPIView RetrieveModelMixin UpdateModelMixin DestroyModelMixin
ListCreateAPIView 群查 更新一条 get post GenericAPIView ListModelMixin mixins.CreateModelMixin

xxxViewset-视图集

常用视图集父类

ViewSetMixin

ViewSetMixin 主要是自定义了 as_view 方法,使可以通过其参数指定 HTTP_METHOD 与函数的映射关系,如 view = MyViewSet.as_view({‘get’: ’list’, ‘post’: ‘create’})

ViewSet

继承自 APIView 和 ViewSetMixin,没有提供任何方法,需要自己写

GenericViewSet

继承 GenericAPIView 和 ViewSetMixin,其中 GenericAPIView 提供了基础方法,可以直接搭配 Mixin 扩展类使用,因此比较常用

ModelViewSet  

继承 GenericViewset,但同时也包括 ListModelMixin、CreateModelMixin 等 mixin 扩展类

源码分析

  1. 视图集都是默认优先继承 ViewSetMixin 类,再继承一个视图类(GenericAPIView 或 APIView)
  2. ViewSetMixin 提供了重写的 as_view() 方法,继承视图集的视图类,配置路由时调用 as_view() 必须传入 请求名 - 函数名 映射关系字典

例如:

# 表示 get 请求会交给 my_get_list 视图函数处理
path('v5/books/', views.BookGenericViewSet.as_view({'get': 'my_get_list'})),

GenericViewSet 示例代码

urls.py

urlpatterns = [
    # View的as_view():将get请求映射到视图类的get方法
    # ViewSet的as_view({'get': 'my_get_list'}):将get请求映射到视图类的my_get_list方法
    path('v5/books/', views.BookGenericViewSet.as_view({'get': 'my_get_list'})),
    path('v5/books/<pk>/', views.BookGenericViewSet.as_view({'get': 'my_get_obj'})),
]

views.py

from rest_framework.viewsets import GenericViewSet
from rest_framework import mixins  #工具集 可以使用list, retrieve等方法

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

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

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

GenericViewSet 与 ViewSet

异同

  1. GenericViewSet 和 ViewSet 都继承了 ViewSetMixin,as_view 都可以配置 请求 - 函数 映射
  2. GenericViewSet 继承的是 GenericAPIView 视图类,用来完成标准的 model 类操作接口
  3. ViewSet 继承的是 APIView 视图类,用来完成不需要 model 类参与,或是非标准的 model 类操作接口,如
    • post 请求在标准的 model 类操作下就是新增接口,登陆的 post 不满足。登陆的 post 请求,并不是完成数据的新增,只是用 post 提交数据,得到的结果也不是登陆的用户信息,而是登陆的认证信息
    • post 请求验证码的接口,不需要 model 类的参与

源码

GenericViewSet

# viewsets.py
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.
    默认情况下,GenericViewSet类不提供任何操作,但是包含通用视图行为的基本集,比如get ohiect和get queryset方法
    """
    pass

# generics.py
class GenericAPIView(views.APIView):
    # ...

# vies.py
class APIView(View):
    # ...

ViewSet


class ViewSet(ViewSetMixin, views.APIView):
    """
    The base ViewSet class does not provide any actions by default.
    默认情况下,基本ViewSet类不提供任何操作。
    """
    pass

# vies.py
class APIView(View):
    # ...

ModelViewSet 示例代码

urls.py

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

views.py

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

    # 删不是数据库,而是该记录中的修改is_delete的值,因此重写默认的destroy函数
    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()          # type: models.Book
        if not instance:
            return APIResponse(1, '删除失败')  # 实际操作,在此之前就做了判断
        instance.is_delete = True
        instance.save()
        return APIResponse(0, '删除成功')

路由组件

因为具有局限性,所以在开发复杂接口时并不是首选。

示例代码如下:

from django.urls import path, include
from rest_framework.routers import SimpleRouter
from . import views

router = SimpleRouter()
# 所有路由与ViewSet视图类的都可以注册,会产生 'v7/books/' 和 'v7/books/<pk>/'
router.register('v7/books', views.BookModelViewSet)

urlpatterns = [
    # router.urls 添加方法一
    # path('', include(router.urls)),
]

# router.urls 添加方法二
# urlpatterns += router.urls