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"}))
]

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

七、django rest_framework源码之视图的更多相关文章

  1. 五、django rest_framework源码之版本控制剖析

    1 绪论 Djangorest_framework的版本控制允许用户更改不同客户端之间的行为,且提供了许多不同的版本控制方案.版本控制由传入的客户端请求确定,可以基于请求URL,也可以基于请求标头. ...

  2. 四、django rest_framework源码之频率控制剖析

    1 绪言 权限判定之后的下一个环节是访问频率控制,本篇我们分析访问频率控制部分源码. 2 源码分析 访问频率控制在dispatch方法中的initial方法调用check_throttles方法开始. ...

  3. 二、django rest_framework源码之认证流程剖析

    1 绪言 上一篇中讲了django rest_framework总体流程,整个流程中最关键的一步就是执行dispatch方法.在dispatch方法中,在调用了一个initial方法,所有的认证.权限 ...

  4. 六、django rest_framework源码之解析器剖析

    1 绪论 网络传输数据只能传输字符串格式的,如果是列表.字典等数据类型,需要转换之后才能使用但是我们之前的rest_framework例子都没有转换就直接可以使用了,这是因为rest_framewor ...

  5. 三、django rest_framework源码之权限流程剖析

    1 绪言 上一篇中分析了认证部分的源码,认证后的下一个环节就是权限判定了.事实上,权限判定肯定要与认证联合使用才行,因为认证部分不会对请求进行禁止或者是允许,而只是根据请求中用户信息进行用户身份判断, ...

  6. 一、django rest_framework源码之总体流程剖析

    1 序言 有如下django代码,视图层: from django.http import HttpResponse from rest_framework.views import APIView ...

  7. $Django cbv源码分析 djangorestframework框架之APIView源码分析

    1 CBV的源码分析 #视图 class login (View): pass #路由 url(r'^books/$', views.login.as_view()) #阅读源码: #左侧工程栏--- ...

  8. Django session 源码流程

    流程 Django session源码流程 首先执行的是SessionMiddleware的init方法 import_module(settings.SESSION_ENGINE) 导入了一个 dj ...

  9. springMVC源码分析--视图AbstractView和InternalResourceView(二)

    上一篇博客springMVC源码分析--视图View(一)中我们介绍了简单介绍了View的结构实现及运行流程,接下来我们介绍一下View的实现类做的处理操作. AbstractView实现了rende ...

随机推荐

  1. spring boot(三):spring data jpa的使用

    @RequestMapping("/queryUserListByPageNativeQuery") public String queryUserListByPageNative ...

  2. Java写的数据库连接池

    原文地址: http://lgscofield.iteye.com/blog/1820521 import java.sql.*; import java.util.Enumeration; impo ...

  3. 简单理解 NP, P, NP-complete和NP-Hard

    P是一类可以通过确定性图灵机(以下简称 图灵机)在多项式时间(Polynomial time)内解决的问题集合. NP是一类可以通过非确定性图灵机( Non-deterministic Turing ...

  4. 【BZOJ】3160: 万径人踪灭 FFT+回文串

    [题意]给定只含'a'和'b'字符串S,求不全连续的回文子序列数.n<=10^5. [算法]FFT+回文串 [题解]不全连续的回文子序列数=回文子序列总数-回文子串数. 回文子串数可以用回文串算 ...

  5. Activity相关知识点总结

    一.Activity状态 Activity有三种状态:active/running.paused.stopped. 1.active/running状态,在当前屏幕时,即用户可见的Activity,位 ...

  6. Shell脚本中字符串判空:使用-z 字符串长度为0时,为真,-n字符串长度不为0,为真。这两个都不靠谱【转】

    最近发现使用  -z   和  -n  来判断字符串判空,或不空时,很不靠谱. 使用下面的方法最可靠: if [ "x${value}" == "x" ]    ...

  7. HDU 2825 Wireless Password

    题目链接:HDU-2825 题意:给出m个单词,要构造出满足包含其中大于等于k个单词的字符串,字符只包括小写字母,问长度为n的这样的串有多少个. 思路:令dp[i][j][k]表示当前已经构造了i个字 ...

  8. Codeforces 859E Desk Disorder 并查集找环,乘法原理

    题目链接:http://codeforces.com/contest/859/problem/E 题意:有N个人.2N个座位.现在告诉你这N个人它们现在的座位.以及它们想去的座位.每个人可以去它们想去 ...

  9. poj1056

    简单题 #include <iostream> #include <string> using namespace std; struct cnode { cnode *pze ...

  10. 如何提高PHP执行效率

    用单引号代替双引号来包含字符串,这样做会更快一些.因为PHP会在双引号包围的字符串中搜寻变量,单引号则不会,注意:只有echo能这么做,它是一种可以把多个字符串当作参数的“函数”(译注:PHP手册中说 ...