【Django学习】(十三)Mixins_各种具体通用类&APIView_ViewSet_GenericViewSet_ModelViewSet类视图继承的父类区别

文章介绍了如何在DjangoREST框架中使用mixins来减少代码重复,包括自定义mixins和使用rest_framework提供的mixins。通过继承GenericAPIView和其他mixins,可以方便地实现列表、详情、创建、更新和删除操作,并讨论了APIView、ViewSet和GenericViewSet的区别。

上一章我们已经学习了如何使用genericapiview实现过滤、排序、分页功能;

这次学习的是如何使用Mixins的各种具体通用类简化代码量

一、自己定义mixins通用类

1、在视图类里,感觉有很多代码都是重复的,是不是可以把公用逻辑提取出来单独封装呢?

答案是可以的!

我们在utils目录下单独创建一个文件mixins.py提取公共部分

#!/usr/bin/python3
# -*- coding: utf-8 -*-
from rest_framework import status
from rest_framework.response import Response


class ListModelMixin:
    def list(self, request, *args, **kwargs):
        qs = self.get_queryset()
        qs = self.filter_queryset(qs)
        page_queryset = self.paginate_queryset(qs)
        if page_queryset is not None:
            serializer_obj = self.get_serializer(instance=page_queryset, many=True)
            return self.get_paginated_response(serializer_obj.data)
        serializer_obj = self.get_serializer(instance=page_queryset, many=True)
        return Response(serializer_obj.data, status=status.HTTP_200_OK)


class CreateModelMixin:
    def create(self, request, *args, **kwargs):
        serializer_obj = self.get_serializer(data=request.data)
        serializer_obj.is_valid(raise_exception=True)
        serializer_obj.save()
        return Response(serializer_obj.data, status=status.HTTP_201_CREATED)


class RetrieveModelMixin:
    def retrieve(self, request, *args, **kwargs):
        pro = self.get_object()
        serializer_obj = self.serializer_class(instance=pro)
        return Response(serializer_obj.data, status=status.HTTP_200_OK)


class UpdateModelMixin:
    def update(self, request, *args, **kwargs):
        # 查出对应id的数据
        query_data = self.get_object()
        serializer_obj = self.serializer_class(instance=query_data, data=request.data)
        serializer_obj.is_valid()
        # 保存更新的数据
        serializer_obj.save()
        return Response(serializer_obj.data, status=status.HTTP_201_CREATED)


class DestroyModelMixin:
    def destroy(self, request, *args, **kwargs):
        ret = {
            "msg": "删除成功!"
        }
        # 根据id查出对应数据
        query_data = self.get_object()
        # 删除指定数据
        query_data.delete()
        # 一般删除数据的输出为None
        return Response(ret, status=status.HTTP_204_NO_CONTENT)

 2、然后在视图文件中导入

from utils import mixins
class ProjectsDetailViews(mixins.RetrieveModelMixin,
                          mixins.UpdateModelMixin,
                          mixins.DestroyModelMixin,
                          GenericAPIView):
    queryset = ProjectsModel.objects.all()
    serializer_class = ProjectModelSerializer

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

    # 更新数据
    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    # 删除数据
    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)


# class ProjectsViews(APIView):
class ProjectsViews(mixins.ListModelMixin,
                    mixins.CreateModelMixin,
                    GenericAPIView):
    # 指定当前类视图需要使用的查询集
    queryset = ProjectsModel.objects.all()
    # 指定当前类视图需要使用的序列化器类
    serializer_class = ProjectModelSerializer
    # lookup_field = 'Id'
    # 声明需要使用的引擎类
    filter_backends = [filters.SearchFilter,
                       filters.OrderingFilter
                       ]
    # 定义需要过滤的字段
    search_fields = ['name', 'id']

    # 定义需要排序的字段
    ordering_fields = ['id', 'name']
    # 声明需要使用的分页引擎
    pagination_class = PageNumberPagination

    # 查询全部数据
    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    # 创建数据
    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

 如图所示,视图类中继承的父类中的顺序要注意:

  • GenericAPIView只能放在最后位置,因为mixins.py公共逻辑文件中很多方法都是需要继承GenericAPIView的,这里面涉及到mro算法
    • 比如获取列表方法中的get_queryset,我们并没有在misins文件中导入对应的包,当在当前文件中找不到这个方法时,就会依次去其他父类中查找,最后在GenericAPIView这个父类中找到
    • 如果GenericAPIView放在最前面,那么可能会导致有些方法找不到

二、使用rest_framework中的mixins通用类

首先我们可以进入到GenericAPIView视图类里去看下,发现里面已经定义好了很多通用接口类

 再去看具体的接口请求类里面看,发现继承的是GenericAPIView和rest_framework中mixins.py里的ListModelMixin等一些类(如下图例子)

class ListAPIView(mixins.ListModelMixin,
                  GenericAPIView):
    """
    Concrete view for listing a queryset.
    """
    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

 GenericAPIView中的ListAPIView中,返回的是rest_framework中mixins.py里的ListModelMixin的list方法

class ListModelMixin:
    """
    List a queryset.
    """
    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

drf框架已经内部定义了一些已经封装好的通用类,而且可以看出代码逻辑和我们自己写的大体上差不多,所以我们可以放心的使用drf框架内的这些封装类以及方法

 导入rest_framework中的mixins包:

# from utils import mixins
from rest_framework import mixins

因为 我们之前定义的通用类名/方法名与drf内置的通用类名/方法名一致,所以视图集内的其他代码可以不用变动

三、使用rest_framework的generics.py文件中的通用类

  • 因为通用类中也继承了GenericAPIView,所以视图类里可以不用再继承了,可以删除
  • 因为通用类CreateAPIView和ListAPIView分别继承了rest_framework中的mixins.CreateModelMixin和mixins.ListModelMixin,分别调用了create方法和list方法,所以在视图类里可以不用再自己写各种接口方法了(下面代码块注释掉的部分)

 

class ProjectsViews(generics.CreateAPIView,
                    generics.ListAPIView,
                    ):
    # 指定当前类视图需要使用的查询集
    queryset = ProjectsModel.objects.all()
    # 指定当前类视图需要使用的序列化器类
    serializer_class = ProjectModelSerializer
    # lookup_field = 'Id'
    # 声明需要使用的引擎类
    filter_backends = [filters.SearchFilter,
                       filters.OrderingFilter
                       ]
    # 定义需要过滤的字段
    search_fields = ['name', 'id']

    # 定义需要排序的字段
    ordering_fields = ['id', 'name']
    # 声明需要使用的分页引擎
    pagination_class = PageNumberPagination

    # 查询全部数据
    # def get(self, request, *args, **kwargs):
    #     return self.list(request, *args, **kwargs)
    #
    # # 创建数据
    # def post(self, request, *args, **kwargs):
    #     return self.create(request, *args, **kwargs)

上面的继承类也可以直接继承generics里的ListCreateAPIView类

class ProjectsViews(generics.ListCreateAPIView):

四、类视图继承的父类区别

1、继承APIView

  1. 如果两个类视图合并,会出现两个get方法会冲突的问题
  2. 如果使用Mixin中提供的拓展方法(action动作),当前DRF是无法识别这些action
  3. class ProjectViewSet(APIView):

2、继承ViewSet视图集

  1. 两个类视图可以合并,尤其是有2个get方法的类视图
  2. 支持这些action,并在定义url路由时,可以在as_view({"请求方法名": "action名称"})
    1. 例如as_view({'get':'retrieve','put':'update','delete':'destroy','patch':'partial_update'})
    2. as_view({'get':'list','post':'create',})
  3. 继承ViewSetMixin()(ViewSetMixin提供的能力,改写as_view(),以便它接受' actions '关键字来执行将HTTP方法绑定到资源上的操作。),views.APIView
    1. 需要自定义action
    2. 并且不支持分页、过滤、排序功能
  4. class ProjectViewSet(viewsets.ViewSet):

 

class ProjectsViewSet(viewsets.ViewSet):
    # 指定当前类视图需要使用的查询集
    queryset = ProjectsModel.objects.all()
    # 指定当前类视图需要使用的序列化器类
    serializer_class = ProjectModelSerializer
    # lookup_field = 'Id'
    # 声明需要使用的引擎类
    filter_backends = [filters.SearchFilter,
                       filters.OrderingFilter
                       ]
    # 定义需要过滤的字段
    search_fields = ['name', 'id']

    # 定义需要排序的字段
    ordering_fields = ['id', 'name']
    # 声明需要使用的分页引擎
    pagination_class = PageNumberPagination

    def retrieve(self, request, *args, **kwargs):
        # 需要自定义action    
        pass

    def list(self, request, *args, **kwargs):
        pass

    def put(self, request, *args, **kwargs):
        pass

    def destroy(self, request, *args, **kwargs):
        pass

    def create(self, request, *args, **kwargs):
        pass

    def partial_update(self, request, *args, **kwargs):
        pass

3、继承GenericViewSet通用视图集

  1. 两个类视图可以合并,尤其是有2个get方法的类视图

  2. 继承 ViewSetMixin, generics.GenericAPIView

    1. ViewSetMixin提供了路由识别action的能力

  3. 支持分页、过滤、排序功能(因为有继承generics.GenericAPIView)

  4. 可以先声明queryset、serializer_class

  5. 未提供action方法

  6. class ProjectViewSet(viewsets.GenericViewSet):

class ProjectsViewSet(
    mixins.RetrieveModelMixin,
    mixins.UpdateModelMixin,
    mixins.DestroyModelMixin,
    mixins.ListModelMixin,
    mixins.CreateModelMixin,
    viewsets.GenericViewSet):
    # 指定当前类视图需要使用的查询集
    queryset = ProjectsModel.objects.all()
    # 指定当前类视图需要使用的序列化器类
    serializer_class = ProjectModelSerializer
    # lookup_field = 'Id'
    # 声明需要使用的引擎类
    filter_backends = [filters.SearchFilter,
                       filters.OrderingFilter
                       ]
    # 定义需要过滤的字段
    search_fields = ['name', 'id']

    # 定义需要排序的字段
    ordering_fields = ['id', 'name']
    # 声明需要使用的分页引擎
    pagination_class = PageNumberPagination
    # 因为继承了mixins里的各种action方法,所以自定义的action就可以不用写了
    # 如果请求方法为get -> retrieve方法
    # def retrieve(self, request, *args, **kwargs):
    #     pass
    # 如果请求方法为get -> list方法
    # def list(self, request, *args, **kwargs):
    #     pass
    #
    # def update(self, request, *args, **kwargs):
    #     pass
    #
    # def destroy(self, request, *args, **kwargs):
    #     pass
    #
    # def create(self, request, *args, **kwargs):
    #     pass
    #
    # def partial_update(self, request, *args, **kwargs):
    #     pass
🔼因为继承了mixins里的各种action方法,所以自定义的action就可以不用写了(上面被注释的代码!)

上面继承了太多的 mixins,我们可以找个全部继承了这些类的子类 来继承,这就是ModelViewSet模型视图集类

 最后可以简化为:

class ProjectsViewSet(viewsets.ModelViewSet):

好的,下面是基于Django的rest_framework框架的ModelViewSet视图的源码解析: 1.基本介绍 ModelViewSet视图是Django框架的rest_framework模块中的一个视图,它继承了GenericAPIView和Mixins,提供了对资源的CRUD操作。ModelViewSet视图中提供了一系列的方法,如list、create、retrieve、update、partial_update、destroy等,这些方法对应着HTTP请求中的GET、POST、PUT、PATCH、DELETE等动词。 2.源码解析 ModelViewSet视图的源码主要涉及到以下几个部分: (1)视图的定义 ``` 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 ``` 从源码可以看出,ModelViewSet继承了CreateModelMixin、RetrieveModelMixin、UpdateModelMixin、DestroyModelMixin和ListModelMixin这些视图混合,以及GenericViewSet。 (2)视图方法的实现 a. list方法 ``` def list(self, request, *args, **kwargs): queryset = self.filter_queryset(self.get_queryset()) page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) serializer = self.get_serializer(queryset, many=True) return Response(serializer.data) ``` list方法用于返回资源列表,它首先通过get_queryset方法获取查询集,然后通过filter_queryset方法对查询集进行过滤,最后通过get_serializer方法将查询集序列化为JSON数据返回给客户端。 b. create方法 ``` def create(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) self.perform_create(serializer) headers = self.get_success_headers(serializer.data) return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) ``` create方法用于创建资源,它首先通过get_serializer方法获取序列化器,然后通过perform_create方法进行资源的创建,最后返回HTTP 201 Created状态码以及资源的JSON数据。 c. retrieve方法 ``` def retrieve(self, request, *args, **kwargs): instance = self.get_object() serializer = self.get_serializer(instance) return Response(serializer.data) ``` retrieve方法用于获取单个资源,它通过get_object方法获取资源实例,然后通过get_serializer方法将资源实例序列化为JSON数据返回给客户端。 d. update方法 ``` def update(self, request, *args, **kwargs): partial = kwargs.pop('partial', False) instance = self.get_object() serializer = self.get_serializer(instance, data=request.data, partial=partial) serializer.is_valid(raise_exception=True) self.perform_update(serializer) return Response(serializer.data) ``` update方法用于更新资源,它首先通过get_object方法获取资源实例,然后通过get_serializer方法获取序列化器,最后通过perform_update方法进行资源的更新,最终返回更新后的资源JSON数据。 e. partial_update方法 ``` def partial_update(self, request, *args, **kwargs): kwargs['partial'] = True return self.update(request, *args, **kwargs) ``` partial_update方法用于部分更新资源,它通过调用update方法,并将partial参数设置为True,进行资源的部分更新。 f. destroy方法 ``` def destroy(self, request, *args, **kwargs): instance = self.get_object() self.perform_destroy(instance) return Response(status=status.HTTP_204_NO_CONTENT) ``` destroy方法用于删除资源,它首先通过get_object方法获取资源实例,然后通过perform_destroy方法进行资源的删除,最终返回HTTP 204 No Content状态码。 3.总结 ModelViewSet视图是Django框架的rest_framework模块中的一个视图,它继承了GenericAPIView和Mixins,提供了对资源的CRUD操作。通过对视图的源码解析,我们可以深入了解ModelViewSet视图的内部实现,从而更好地理解和使用该视图
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

高老庄里的高小翠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值