• 七、django rest_framework源码之视图


    1 绪言

      当大家看大这篇博文的时候,应该对Django rest_framework中的CBV有所了解了,大致来说就是通过定义类来继承APIView类,并在类中定义get、post、put、delete等对应于请求方法的方法,当请求来的时候会自动反射到相应的方法并执行,路由中需要配置类的as_view()的方式来配置路由,至于如何拿到请求方法并对应执行我们自定义类中的方法,在我的rest_framework系列的第一篇博文中有详细的分析,这里不再多说。这一篇博文我们来研究一下视图类。

    2 视图进化

    2.1 第一代视图

      假设我们在做一个电商项目,有一个商品类(GoodsModel),现在要写一个视图进行get和post,通过继承rest_framework的APIView类我们可以写出一下代码:

    from .models import GoodsModel
    from rest_framework import serializers
    from rest_framework.response import Response
    
    class GoodsSerializer(serializers.ModelSerializer):
    class Meta:
    model = GoodsModel
    fields = "__all__"
    class GoodDetailView(APIView):#单个商品
      def get(self, request , pk):
        ret = GoodsModel.objects.filter(id=pk).first()
        good_ser = GoodsSerializer(ret)
        return Response(good_ser.data)
      def post(self, request):
        ser = GoodsSerializer(data=request.data)
        if ser.is_valid():
          ser.save()
          return HttpResponse('您提交的post请求数据符合要求,已成功录入后台')
        return Response(ser.errors)
    class GoodsView(APIView):#所有商品
      def get(self, request):
        ret = GoodsModel.objects.all()
        good_ser = GoodsSerializer(ret, many=True)
        return Response(good_ser.data)

    路由配置如下:

    urlpatterns = [
    ……
    url(r'^goods/(?P<pk>d+)/$', GoodDetailView.as_view()) ,
    url(r'^goods/$', GoodsView.as_view())
    
    ]

      上述代码针对单个商品和商品集写了两个视图类,我们进一步往下想,电商项目难道只有一个商品类吗?肯定不是,还会有订单、商品类型,甚至是生产厂家等等,有个十几二十个模型类就很正常了,每一种模型都要写类似上面的两个类,每个类都要有对应的请求方法函数,那就是几十上百个方法了……但仔细想想,要写的几十个类的功能都差不多,无非就是增删改查,重复类似的代码几十次想想都恐怖。那有没有什么方法将这些增删改查的逻辑封装成类,我们用的时候只需要继承这些类,然后调用即可呢?有!如果我们把上面继承APIView,然后自己实现增删改查的视图称为第一代试图,那么我们下面要介绍的第二代视图就有封装这些功能方法。

    2.2 第二代视图

      第一代的试图类必须继承APIView,第二代视图就必须继承GenericAPIView,GenericAPIView继承于APIView,在其父类的基础上为列表视图和详情视图添加了常用的行为。当然,第二代视图的增删改查功能方法并不在GenericAPIView类中, rest_framework.mixins模块中,包括以下五个类,分别对应对集合的查询和对单个实例的增删改查:
      ListModelMixin——批量查询(get)

      CreateModelMixin——新增数据(post)

      UpdateModelMixin——更新数据(put/patch)

      RetrieveModelMixin——查看单条数据(get)

      DestroyModelMixin——删除单条数据(delete)

      上面这五个类每个类里面都要一个对应的方法来实现相应的功能,ListModelMixin类中有一个名为list的类实现批量查询,例如:CreateModelMixin类中有一个名为create的类实现插入数据功能。可以看出,类名与类里面的实现功能的方法名是一一对应的。
    当我们再写对GoodModel的视图时,只需要继承上面的方法就好了。现在,我们把一代的视图改写为二代视图,代码如下:

    class GoodDetailView(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, GenericAPIView):#单个商品
        queryset = GoodsModel.objects.all()
        serializer_class = GoodsSerializer
        def get(self, request, *args, **kwargs):
            return self.retrieve(request, *args, **kwargs)
        def post(self, request, *args, **kwargs):
            return self.update(request, *args, **kwargs)
    
    class GoodsView(ListModelMixin, CreateModelMixin, GenericAPIView):#所有商品
        queryset = GoodsModel.objects.all()
        serializer_class = GoodsSerializer
        def get(self, request, *args, **kwargs):
          return self.list(request, *args, **kwargs)        

      一代视图和二代视图在功能上是完全等效的,但明显二代视图代码要清晰简洁得多。不过,细细一看,两个类中还是有重复的代码,还是需要手动去调用实现增删改查的方法。如果把这些类的功能封装在一起,然后统一继承这个类,岂不是更加省事?是的!这就是第三代视图。

    2.3 第三代视图

      第二代的视图时将实现增删改查的类与GenericAPIView这个视图是分开的,所以都要去继承,第三带来是将第二代中实现增删改查的五个类进行不同组合,然后与GenericAPIView类一起作为父类去继承。第三代的视图是放在rest_framework.generics中的。
      将上述的第二代视图改装成第三代视图:

    class GoodDetailView(RetrieveUpdateAPIView):
        queryset = GoodsModel.objects.all()
        serializer_class = GoodsSerializer
    
    class GoodsView(ListAPIView):#所有商品
        queryset = GoodsModel.objects.all()
        serializer_class = GoodsSerializer

      第三代视图够简洁了吧?不够,本质上来说,两个视图类都是针对同一个模型,只不过一个是针对单个对象,一个是多个对象,功能逻辑上也是类似的。那能不能合成一个类呢?可以,所以还有第四代视图。

    2.4 第四代视图

      第四代视图就是将二代视图中所有功能类与GenericAPIView放在一起作为父类,然后你以为类里面做了很复杂的反射代码去执行对应的请求方法吗?没有。下面是四代视图的源码:

    class ModelViewSet(mixins.CreateModelMixin,
                                    mixins.RetrieveModelMixin,
                                    mixins.UpdateModelMixin,
                                    mixins.DestroyModelMixin,
                                    mixins.ListModelMixin,
                                    GenericViewSet):
    
        Pass

      看到没,四代视图里面只有一个pass语句,换句话说,四代视图屁事没做。那么它是怎么找到对应的方法呢?答:通过路由。所以四代视图路由设置上与前面三代视图是不同的。我们通过实际例子感受一下,继续把上面的三代视图改写成四代视图:

    class GoodsView(ModelViewSet):#所有商品
        queryset = GoodsModel.objects.all()
        serializer_class = GoodsSerializer

      你没有看错,四代视图就一个类,三行代码。接下来是四代视图的路由配置:

    urlpatterns = [
        ……
        url(r'^goods/(?P<pk>d+)/$', GoodsView.as_view({"get":"retrieve","post":"create"})) ,
        url(r'^goods/$', GoodsView.as_view({"get":"list"}))
    ]

      到此,视图部分就介绍完了,主要就是搞清楚继承关系,源码并不复杂。

  • 相关阅读:
    页面滚屏截图工具推荐
    java总结第二次(剩余内容)//类和对象1
    happy birthday to tbdd tomorrow
    数组增删改查及冒泡
    三个循环方面程序
    三个入门小程序
    java总结第二次//数组及面向对象
    Java总结第一次//有些图片未显示,文章包含基础java语言及各种语句
    后台验证url是不是有效的链接
    img 鼠标滑上后图片放大,滑下后图片复原
  • 原文地址:https://www.cnblogs.com/chenhuabin/p/9991293.html
Copyright © 2020-2023  润新知