Django CBV模式的源码解析

通常来说,http请求的本质就是基于Socket

Django的视图函数,可以基于FBV模式,也可以基于CBV模式。

基于FBV的模式就是在Django的路由映射表里进行url和视图函数的关联,而基于CBV的模式则是在views.py文件中定义视图类,在视图类中视图函数,如get,post,put,delete等

使用Django新建一个项目,新建一个路由映射

  1. from django.conf.urls import url
  2. from django.contrib import admin
  3. from app01 import views
  4. urlpatterns = [
  5. url(r'^cbv/$',views.CBV.as_view())
  6. ]

对应的views.py文件内容:

  1. from django.shortcuts import render,HttpResponse
  2. from django.views import View
  3. class CBV(View):
  4. def get(self,request):
  5. return HttpResponse("GET")
  6. def post(self,request):
  7. return HttpResponse("POST")

启动项目,使用浏览器请求URLhttp://127.0.0.1:8000/cbv/,浏览器显示结果为:

请求到达Django会先执行Django中间件里的方法,然后进行进行路由匹配。

在路由匹配完成后,会执行CBV类中的as_view方法。

CBV中并没有定义as_view方法,由于CBV继承自Django的View,所以会执行Django的View类中的as_view方法

Django的View类的as_view方法的部分源码

  1. class View(object):
  2. """
  3. Intentionally simple parent class for all views. Only implements
  4. dispatch-by-method and simple sanity checking.
  5. """
  6. http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
  7. def __init__(self, **kwargs):
  8. """
  9. Constructor. Called in the URLconf; can contain helpful extra
  10. keyword arguments, and other things.
  11. """
  12. # Go through keyword arguments, and either save their values to our
  13. # instance, or raise an error.
  14. for key, value in six.iteritems(kwargs):
  15. setattr(self, key, value)
  16. @classonlymethod
  17. def as_view(cls, **initkwargs):
  18. """
  19. Main entry point for a request-response process.
  20. """
  21. for key in initkwargs:
  22. if key in cls.http_method_names:
  23. raise TypeError("You tried to pass in the %s method name as a "
  24. "keyword argument to %s(). Don't do that."
  25. % (key, cls.__name__))
  26. if not hasattr(cls, key):
  27. raise TypeError("%s() received an invalid keyword %r. as_view "
  28. "only accepts arguments that are already "
  29. "attributes of the class." % (cls.__name__, key))
  30. def view(request, *args, **kwargs):
  31. self = cls(**initkwargs)
  32. if hasattr(self, 'get') and not hasattr(self, 'head'):
  33. self.head = self.get
  34. self.request = request
  35. self.args = args
  36. self.kwargs = kwargs
  37. return self.dispatch(request, *args, **kwargs)
  38. view.view_class = cls
  39. view.view_initkwargs = initkwargs
  40. # take name and docstring from class
  41. update_wrapper(view, cls, updated=())
  42. # and possible attributes set by decorators
  43. # like csrf_exempt from dispatch
  44. update_wrapper(view, cls.dispatch, assigned=())
  45. return view

从View的源码可以看出,在View类中,先定义了http请求的八种方法

  1. http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

as_view方法中进行判断,如果请求的方法没在http_method_names中,则会抛出异常,这里的cls实际上指的是自定义的CBV类

接着as_view方法中又定义view方法,在view方法中对CBV类进行实例化,得到self对象,然后在self对象中封装浏览器发送的request请求

  1. self = cls(**initkwargs)

最后又调用了self对象中的dispatch方法并返回dispatch方法的值来对request进行处理

此时,由于self对象就是CBV实例化得到,所以会先执行自定义的CBV类中的dispatch方法。如果CBV类中没有定义dispatch方法则执行Django的View中的dispatch方法

Django的View中的dispatch方法源码

  1. class View(object):
  2. """
  3. 中间省略
  4. """
  5. def dispatch(self, request, *args, **kwargs):
  6. # Try to dispatch to the right method; if a method doesn't exist,
  7. # defer to the error handler. Also defer to the error handler if the
  8. # request method isn't on the approved list.
  9. if request.method.lower() in self.http_method_names:
  10. handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
  11. else:
  12. handler = self.http_method_not_allowed
  13. return handler(request, *args, **kwargs)

在dispatch方法中,把request.method转换为小写再判断是否在定义的http_method_names中,如果request.method存在于http_method_names中,则使用getattr反射的方式来得到handler

在这里的dispatch方法中,self指的是自定义的CBV类实例化得到的对象

从CBV类中获取request.method对应的方法,再执行CBV中的方法并返回

由此,可以知道如果在Django项目中使用CBV的模式,实际上调用了getattr的方式来执行获取类中的请求方法对应的函数

结论:

  1. CBV基于反射实现根据请求方式不同,执行不同的方法

自定义dispatch方法

如果想在基于CBV模式的项目中在请求某个url时执行一些操作,则可以在url对应的类中定义dispatch方法

修改views.py文件

  1. class CBV(View):
  2. def dispatch(self, request, *args, **kwargs):
  3. func = getattr(self,request.method.lower())
  4. return func(request,*args,**kwargs)
  5. def get(self,request):
  6. return HttpResponse("GET")
  7. def post(self,request):
  8. return HttpResponse("POST")

也可以使用继承的方式重写dispatch方法:

  1. class CBV(View):
  2. def dispatch(self, request, *args, **kwargs):
  3. print("before")
  4. res = super(CBV, self).dispatch(request, *args, **kwargs)
  5. print("after")
  6. return res
  7. def get(self,request):
  8. return HttpResponse("GET")
  9. def post(self,request):
  10. return HttpResponse("POST")

刷新浏览器,Django后台打印结果如下:

浏览器页面结果

同理,如果有基于CBV的多个类,并且有多个类共用的功能,为了避免重复,可以单独定义一个类,在这个类中重写dispatch方法,然后让url对应的视图类继承这个类

修改urls.py文件

  1. from django.conf.urls import url
  2. from django.contrib import admin
  3. from app01 import views
  4. urlpatterns = [
  5. url(r'^cbv1/$',views.CBV1.as_view()),
  6. url(r'^cbv2/$',views.CBV2.as_view()),
  7. ]

views.py文件内容

  1. from django.shortcuts import render,HttpResponse
  2. from django.views import View
  3. class BaseView(object):
  4. def dispatch(self, request, *args, **kwargs):
  5. func = getattr(self, request.method.lower())
  6. return func(request, *args, **kwargs)
  7. class CBV1(BaseView,View):
  8. def get(self,request):
  9. return HttpResponse("CBV1 GET")
  10. def post(self,request):
  11. return HttpResponse("CBV1 POST")
  12. class CBV2(BaseView,View):
  13. def get(self,request):
  14. return HttpResponse("CBV2 GET")
  15. def post(self,request):
  16. return HttpResponse("CBV2 POST")

通过python的面向对象可以知道,请求到达视图类时,会先执行CBV1和CBV2类中的dispatch方法,然而CBV1和CBV2类中并没有dispatch方法,则会按照顺序在父类中查找dispatch方法,此时就会执行BaseView类中的dispatch方法了

用浏览器请求urlhttp://127.0.0.1:8000/cbv1/,浏览器页面显示

用浏览器请求urlhttp://127.0.0.1:8000/cbv2/,浏览器页面显示

源码解析Django CBV的本质的更多相关文章

  1. Django生命周期 URL ----> CBV 源码解析-------------- 及rest_framework APIView 源码流程解析

    一.一个请求来到Django 的生命周期   FBV 不讨论 CBV: 请求被代理转发到uwsgi: 开始Django的流程: 首先经过中间件process_request (session等) 然后 ...

  2. 2.CBV和类视图as_view源码解析

    一.FBV和CBV # 视图基于函数开发 FBV: function.base.views # 视图基于类开发 CBV: class .base .views #Python是一个面向对象的编程语言, ...

  3. django之admin源码解析

    解析admin的源码 第一步:项目启动,加载settings文件中的 INSTALLED_APPS 里边有几个app就加载几个,按照注册顺序来执行. 第二步:其中加载的是admin.py,加载每一个a ...

  4. CBV流程之View源码解析

    CBV是基于反射实现根据请求方式不同,执行不同的方法. 请求流程:view源码解析 1.urls.py :请求一定来执行视图下的as_view方法.也可以直接点击as_view()来找源码. 2.vi ...

  5. CBV源码与APIView源码解析

    一.CBV源码解析 在我们写cbv的时候在url中和fbv的区别就是是否调用了as_view()方法,所以关键入手点就是这个方法 @classonlymethod # 这是类的绑定方法,这个cls是我 ...

  6. django -admin 源码解析

    admin源码解析 单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单 ...

  7. Django框架 之 admin管理工具(源码解析)

    浏览目录 单例模式 admin执行流程 admin源码解析 单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在 ...

  8. Django 之 admin组件使用&源码解析

    admin组件使用 Django 提供了基于 web 的管理工具. Django 自动管理工具是 django.contrib 的一部分.可以在项目的 settings.py 中的 INSTALLED ...

  9. Django settings源码解析

    Django settings源码 Django中有两个配置文件 局部配置:配置文件settings.py,即项目同名文件夹下的settings.py文件 全局配置:django内部全局的配置文件se ...

随机推荐

  1. React react-fastclick-alt 移动端点击

    1. Install npm install --save-dev react-fastclick-alt 2. 用法 将元素或者component放在  <FastClick>...&l ...

  2. Quill 富文本编辑器

    Quill 富文本编辑器 https://quilljs.com/ https://github.com/quilljs/quill https://github.com/quilljs/awesom ...

  3. node 全局对象global —— 记录在线人员

    最近做毕设的时候,在做查看在线人员这个功能的时候,一直卡顿,我的思路是数据库保存 是否在线 字段,可以在登录时和退出系统修改状态,但如果用户之间关闭窗口时候就没办法向后台发出修改在线状态的请求.我想到 ...

  4. P1438 无聊的数列 (差分+线段树)

    题目 P1438 无聊的数列 解析: 先考虑修改,用差分的基本思想,左端点加上首项\(k\),修改区间\((l,r]\)内每个数的差分数组都加上公差\(d\),最后的\(r+1\)再减去\(k+(r- ...

  5. CSS3基础入门03

    CSS3 基础入门03 线性渐变 在css3当中,通过渐变属性实现之前只能通过图片实现的渐变效果.渐变分为线性渐变和径向渐变以及重复渐变三种.线性渐变的模式主要是颜色从一个方向过渡到另外一个方向,而径 ...

  6. 【地图功能开发系列:二】根据地址名称通过百度地图API查询出坐标

    根据地址名称通过百度地图API查询出坐标 百度地图ApiUrl string url = "http://api.map.baidu.com/geocoder?address={0}& ...

  7. HDU - 3247 Resource Archiver (AC自动机,状压dp)

    \(\quad\)Great! Your new software is almost finished! The only thing left to do is archiving all you ...

  8. noi.ac309 Mas的童年

    题目链接 题面 题目描述 \(Mas\)完成了一天的工作,走在回家的路上,看着路边的景色,他想起来自己的童年. 许许多多的记忆交错,丝丝缕缕的牵扯着\(Mas\). 在回忆的深处,\(Mas\)想起来 ...

  9. eclipse安装cucumber插件

    help-install new software-add name= c location= http://cucumber.github.com/cucumber-eclipse/update-s ...

  10. SNMP mib文件说明

    MIB file的开始和结束 所有的MIB file的都以DEFINITIONS ::= BEGIN关键字开始,以END结束.我们所有添加的节点均应在此之间. XXX-TEST-MIB DEFINIT ...