django类视图as_view()⽅法解析
使⽤视图函数时,django完成URL解析之后,会直接把request对象以及URL解析器捕获的参数(⽐如re_path中正则表达捕获的位置参数或关键字参数)丢给视图函数,但是在类视图中,这些参数不能直接丢给⼀个类,所以就有了as_view⽅法,这个⽅法只做⼀件事就是返回⼀个闭包,这个闭包像视图函数⼀样接收url解析器传送过来的参数。
先摆个例⼦放开头,以供参考:
# urls.py
from blog.views import IndexView
urlpatterns = [
re_path(r"^$", IndexView.as_view(), name="index"),
]
1、⾸先了解path和re_path的执⾏逻辑
进到path或re_path的定义处,可以看到他们都是partial类的实例,所以path和re_path都是对象,⽽不是普通函数。
当启动django项⽬时,程序执⾏到urlpatterns时,urlpatterns列表中的各项依次得到执⾏,由于re_path和path都是对象,当对象像函数⼀样调⽤时,其实是调⽤对象中的__call__⽅法,执⾏的结果就是,每个path或re_path的调⽤都返回⼀个URLPattern类的实例对象(路径为solvers.URLPattern),如果打印⼀下re_path的执⾏结果,得到如下结果:
print(re_path(r"^$", IndexView.as_view(), name="index"))
执⾏结果:
>> <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⽅法的完整源码
@classonlymethod
def as_view(cls, **initkwargs):"""Main entry point for a request-response process."""
for key in initkwargs:
正则匹配到第一个关键字就停止if key in cls.http_method_names:
raise TypeError("You tried to pass in the %s method name as a "
"keyword argument to %s(). Don't do that."
% (key, cls.__name__))
if not hasattr(cls, key):
raise TypeError("%s() received an invalid keyword %r. as_view "
"only accepts arguments that are already "
"attributes of the class." % (cls.__name__, key))
def view(request, *args, **kwargs):
self = cls(**initkwargs) # 实例化⼀个类视图对象
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head =
self.setup(request, *args, **kwargs) # 初始化实例属性,保存外⾯传来的参数
if not hasattr(self, 'request'):
raise AttributeError(
"%s instance has no 'request' attribute. Did you override "
"setup() and forget to call super()?" % cls.__name__
)
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs
# take name and docstring from class
update_wrapper(view, cls, updated=())
# and possible attributes set by decorators
# like csrf_exempt from dispatch
update_wrapper(view, cls.dispatch, assigned=())
return view
上⾯的代码有点多,但是有很多代码对理解as_view核⼼作⽤来说是⽆关紧要的,下⾯把代码提炼⼀下:
@classonlymethod
def as_view(cls, **initkwargs):
def view(request, *args, **kwargs):
self = cls(**initkwargs) # 实例化⼀个类视图对象,cls指的就是我们⾃定义的类视图,⽐如开头例⼦中的IndexView,所以self指的就是IndexView的⼀个实例if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head =
self.setup(request, *args, **kwargs) # 初始化实例属性
if not hasattr(self, 'request'):
raise AttributeError(
"%s instance has no 'request' attribute. Did you override "
"setup() and forget to call super()?" % cls.__name__
)
return self.dispatch(request, *args, **kwargs)
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函数内部执⾏逻辑
def view(request, *args, **kwargs):
self = cls(**initkwargs) # 实例化⼀个类视图对象,cls指的是我们⾃定义的类视图,⽐如开头例⼦中的IndexView,所以self指的就是IndexView的⼀个实例if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head =
self.setup(request, *args, **kwargs) # 初始化实例属性
if not hasattr(self, 'request'):
raise AttributeError(
"%s instance has no 'request' attribute. Did you override "
"setup() and forget to call super()?" % cls.__name__
)
return self.dispatch(request, *args, **kwargs)
view函数主要做了两件事情,⼀是实例化⼀个类视图对象,这个容易理解,是哪个类视图对象接收了请求那就实例化哪个。⼆是调⽤dispatch⽅法,根据http请求⽅法(⽐如get,post)分派处理函数,dispatch⽅法逻辑⽐较简单,但是却是理解类视图执⾏逻辑的关键点。先看下源码:
def dispatch(self, request, *args, **kwargs):
# Try to dispatch to the right method; if a method doesn't exist,
# defer to the error handler. Also defer to the error handler if the
# request method isn't on the approved list.
hod.lower() in self.http_method_names: # 把http⽅法改为⼩写,并判断该⽅法是否是合法的http⽅法
handler = getattr(self, hod.lower(), self.http_method_not_allowed) # 在类视图中到对应的处理⽅法,返回该⽅法的引⽤给handler else:
handler = self.http_method_not_allowed
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⽅法,并返回执⾏结果。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论