使用视图函数时,django完成URL解析之后,会直接把request对象以及URL解析器捕获的参数(比如re_path中正则表达捕获的位置参数或关键字参数)丢给视图函数,但是在类视图中,这些参数不能直接丢给一个类,所以就有了as_view方法,这个方法只做一件事就是返回一个闭包,这个闭包像视图函数一样接收url解析器传送过来的参数。

先摆个例子放开头,以供参考:

  1. # urls.py
  2. from blog.views import IndexView
  3.  
  4. urlpatterns = [
  5. re_path(r"^$", IndexView.as_view(), name="index"),
  6. ]

1、首先了解path和re_path的执行逻辑

进到path或re_path的定义处,可以看到他们都是partial类的实例,所以path和re_path都是对象,而不是普通函数。

当启动django项目时,程序执行到urlpatterns时,urlpatterns列表中的各项依次得到执行,由于re_path和path都是对象,当对象像函数一样调用时,其实是调用对象中的__call__方法,执行的结果就是,每个path或re_path的调用都返回一个URLPattern类的实例对象(路径为django.urls.resolvers.URLPattern),如果打印一下re_path的执行结果,得到如下结果:

  1. print(re_path(r"^$", IndexView.as_view(), name="index"))
  2. 执行结果:
  3. >> <URLPattern '^$' [name='index']> # 返回一个URLPattern对象

来看看URLPattern类的定义:

可以看到,URLPattern类__init__方法中的各个参数基本上就对应了传入path或re_path中的参数,其中有个callback属性,就是保存了回调函数的引用。而在path或re_path执行的时候,第二个参数传入的是as_view()(注意传入的不是as_view,而是as_view(),as_view()会立即执行),as_view()执行完成后,返回一个闭包,所以,callback保存的是这个闭包的引用。每次当请求来临时,url解析器完成url的解析,匹配到相应的回调函数,然后执行。

这里要提醒一点是,as_view只会执行一次,就是django在项目启动后,之后所有请求的处理都是由as_view返回的闭包(也就是URLPattern实例对象中的回调函数)执行。

2、再来看看上面说的闭包是什么

首先给出as_view方法的完整源码

  1. @classonlymethod
  2. def as_view(cls, **initkwargs):"""Main entry point for a request-response process."""
  3. for key in initkwargs:
  4. if key in cls.http_method_names:
  5. raise TypeError("You tried to pass in the %s method name as a "
  6. "keyword argument to %s(). Don't do that."
  7. % (key, cls.__name__))
  8. if not hasattr(cls, key):
  9. raise TypeError("%s() received an invalid keyword %r. as_view "
  10. "only accepts arguments that are already "
  11. "attributes of the class." % (cls.__name__, key))
  12.  
  13. def view(request, *args, **kwargs):
  14. self = cls(**initkwargs) # 实例化一个类视图对象
  15. if hasattr(self, 'get') and not hasattr(self, 'head'):
  16. self.head = self.get
  17. self.setup(request, *args, **kwargs) # 初始化实例属性,保存外面传来的参数
  18. if not hasattr(self, 'request'):
  19. raise AttributeError(
  20. "%s instance has no 'request' attribute. Did you override "
  21. "setup() and forget to call super()?" % cls.__name__
  22. )
  23. return self.dispatch(request, *args, **kwargs)
  24. view.view_class = cls
  25. view.view_initkwargs = initkwargs
  26.  
  27. # take name and docstring from class
  28. update_wrapper(view, cls, updated=())
  29.  
  30. # and possible attributes set by decorators
  31. # like csrf_exempt from dispatch
  32. update_wrapper(view, cls.dispatch, assigned=())
  33. return view

上面的代码有点多,但是有很多代码对理解as_view核心作用来说是无关紧要的,下面把代码提炼一下:

  1. @classonlymethod
  2. def as_view(cls, **initkwargs):
  3. def view(request, *args, **kwargs):
  4. self = cls(**initkwargs) # 实例化一个类视图对象,cls指的就是我们自定义的类视图,比如开头例子中的IndexView,所以self指的就是IndexView的一个实例
  5. if hasattr(self, 'get') and not hasattr(self, 'head'):
  6. self.head = self.get
  7. self.setup(request, *args, **kwargs) # 初始化实例属性
  8. if not hasattr(self, 'request'):
  9. raise AttributeError(
  10. "%s instance has no 'request' attribute. Did you override "
  11. "setup() and forget to call super()?" % cls.__name__
  12. )
  13. return self.dispatch(request, *args, **kwargs)
  14. return view # 这就是上面说的闭包

可以看到as_view的定义中又定义了一个view函数,该函数接收三个参数,第一个是request对象,第二个是url解析器捕获的url中的位置参数,第三个是url解析器捕获的url中的关键字参数。返回的view函数就是上面所说的闭包。

先不看view函数内部的执行逻辑,而只关注django接收到请求后的处理逻辑。当django项目启动,调用path或re_path返回URLPattern实例对象,同时as_view函数得到执行,并返回view函数的引用,传递给URLPattern实例对象的callback属性,此时as_view方法的使命完成,之后每次当django接受到浏览器发来的请求,url解析器解析url后,将request对象和url中捕获的参数传递给匹配到的回调函数(即view函数),由view函数执行后续操作。

3、再看view函数内部执行逻辑

  1. def view(request, *args, **kwargs):
  2. self = cls(**initkwargs) # 实例化一个类视图对象,cls指的是我们自定义的类视图,比如开头例子中的IndexView,所以self指的就是IndexView的一个实例
  3. if hasattr(self, 'get') and not hasattr(self, 'head'):
  4. self.head = self.get
  5. self.setup(request, *args, **kwargs) # 初始化实例属性
  6. if not hasattr(self, 'request'):
  7. raise AttributeError(
  8. "%s instance has no 'request' attribute. Did you override "
  9. "setup() and forget to call super()?" % cls.__name__
  10. )
  11. return self.dispatch(request, *args, **kwargs)

view函数主要做了两件事情,一是实例化一个类视图对象,这个容易理解,是哪个类视图对象接收了请求那就实例化哪个。二是调用dispatch方法,根据http请求方法(比如get,post)分派处理函数,dispatch方法逻辑比较简单,但是却是理解类视图执行逻辑的关键点。先看下源码:

  1. def dispatch(self, request, *args, **kwargs):
  2. # Try to dispatch to the right method; if a method doesn't exist,
  3. # defer to the error handler. Also defer to the error handler if the
  4. # request method isn't on the approved list.
  5. if request.method.lower() in self.http_method_names: # 把http方法改为小写,并判断该方法是否是合法的http方法
  6. handler = getattr(self, request.method.lower(), self.http_method_not_allowed) # 在类视图中找到对应的处理方法,返回该方法的引用给handler
  7. else:
  8. handler = self.http_method_not_allowed
  9. return handler(request, *args, **kwargs) # 执行相应的方法

很简单了,先把http方法改为小写,然后判断该方法是否在http_method_names列表中(该列表保存了所有合法的http方法的小写名称),如果判断请求方法是合法的,就从我们自定义的类视图对象中获取到该方法,将引用传给handler,然后返回该方法执行的结果。举例:浏览器发送来一个get请求,get存在于http_method_names列表中,所以是个合法的http方法,此时通过getattr获取到自定义类视图中的get方法,并将get方法的引用传给handler(所以我们需要在自定义类视图中定义get方法,否则dispatch找不到get方法,比如开头的例子中,我们需要在IndexView类中定义get方法),最后执行get方法,并返回执行结果。

django类视图as_view()方法解析的更多相关文章

  1. django类视图简单使用和源码解析

    django的类视图,CBV: 我们在开始接触django的时候,习惯于使用函数编写视图,即FBV.使用FBV时,我们只需要在路由匹配时,对应的路由下找到这个函数就可以了,这样做看似很和谐,但是有的时 ...

  2. 补充01 Django 类视图

    视图 函数视图[Function Base View] 以函数的方式定义的视图称为函数视图,函数视图便于理解.但是遇到一个视图对应的路径提供了多种不同HTTP请求方式的支持时,便需要在一个函数中编写不 ...

  3. django类视图的装饰器验证

    django类视图的装饰器验证 django类视图的get和post方法是由View内部调用dispatch方法来分发,最后调用as_view来完成一个视图的流程. 函数视图可以直接使用对应的装饰器 ...

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

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

  5. Django url中可以使用类视图.as_view()进行映射的原因

    说明:在练习天天生鲜项目时,对利用类视图去与正则匹配到的url做映射有点疑惑,经过查看他人博客以及自我分析算是整明白了,所以记录一下 参考:https://www.zmrenwu.com/post/5 ...

  6. Django 类视图

    引文 所有的类视图都继承django.views.generic.base.View类. 在URLconf中简单的使用通用视图 如果只是简单的做一些属性修改,可以使用as_view()方法,如下所示: ...

  7. django类视图的使用

    1 类视图引入 以函数的方式定义的视图称为函数视图,函数视图便于理解. 但是遇到一个视图对应的路径提供了多种不同HTTP请求方式的支持时,便需要在一个函数中编写不同的业务逻辑,代码可读性与复用性都不佳 ...

  8. Flask05 cookie、类视图、方法视图、自己的404页面

    1 什么是cookie 就是网站存放到你浏览器中的一部分固定内容:当你下次访问我这个网站的时候,你会把之前我存放到你浏览器中的数据带回来给我        你要先登录(用户名.密码) ->   ...

  9. django 类视图的使用

    使用django框架也有挺长时间了,但是一直都没有用过django的类视图,因为之前跟着网上教程学习时,觉得类视图是进阶的知识,可能目前还达不到吧 但今天在做项目的时候用到了,感觉真的太方便了吧,而且 ...

随机推荐

  1. 制作基于软盘的Linux系统

    制作基于软盘的Linux系统(张宏伟.欧阳平平 2001年07月26日 11:22) 嵌入式Linux由一个几百KB的kernel(内核)和一些根据需要进行定制的系统模块组成.由于Linux是开放源代 ...

  2. System.Net.WebRequest.cs

    ylbtech-System.Net.WebRequest.cs 发出对统一资源标识符(URI)的请求.这是一个 abstract 类. 1.返回顶部 1. #region 程序集 System, V ...

  3. 一台服务器部署多台tomcat

    如题,多个项目部署在一台服务器.减少容错性,觉得分开部署,这样一个tomcat挂了不会影响另一个项目.看配置和应用大小决定数量,一般四五个没问题,也有单台服务器部署8个tomcat稳定运行的. 下面记 ...

  4. Angular常用命令:

    新建项目: ng new angualrdermo08 --skip-install 创建需要的组件: ng g component home

  5. LeetCode_292. Nim Game

    292. Nim Game Easy You are playing the following Nim Game with your friend: There is a heap of stone ...

  6. jvm 虚拟机字节码指令表(转)

        

  7. python那些事儿

    一.探索python 1.尝试安装python3 https://www.python.org/downloads/mac-osx/ 2.问题 安装了3.7,但是python -V还显示2.7.10. ...

  8. Docker:学习笔记(1)——基础概念

    Docker:学习笔记(1)——基础概念 Docker是什么 软件开发后,我们需要在测试电脑.客户电脑.服务器安装运行,用户计算机的环境各不相同,所以需要进行各自的环境配置,耗时耗力.为了解决这个问题 ...

  9. 【嵌入式硬件Esp32】ESP32 正确下载姿势

    程序的正确下载步骤,以8M flash为例子: 一.硬件连接 ESP32 的运行状态主要由 GPIO0 决定 二.ESP32 Flash 地址配置 ESP32 在编译时,通过 make menucon ...

  10. CEIWEI USBMonitor USB监控精灵 v2.3.2 USB过滤驱动 USB监控

    CEIWEI USBMonitor USB监控精灵 是一款监控USB端口协议分析软件,用于监控和分析USB设备协议,可以拦截.记录USB软件程序操作USB设备的In.Out数据包.支持监控分析USB票 ...