Django 有FBV(基于函数视图)和CBV(基于类视图),分别是这样用的:
# 函数视图
path(..., function_view, ...)
# 类视图
path(..., ClassView.as_view(), ...)这个 as_view() 很有意思,我们通过源码来看看它是如何把类转化成函数的。
源码不是很长,全贴出来如下所示:
class 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.get
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() 是个类方法,它的第一个参数 cls 表示类本身,跟实例方法的 self 差不多,都是自动传入的。
进入 as_view() 后首先对传入的参数做简单的校验,避免传入的参数将类自己的关键函数名覆盖掉,或者传入类中没定义的属性。开头这个 for 循环就是干这个用的。
接着 as_view() 内部又定义了一个 view() 函数,它接收的参数和普通的函数视图是相同的: request 对象以及从 url 获取的 args 和 kwargs 参数。我们挑重点看它在干什么:
def view(request, *args, **kwargs):
self = cls(**initkwargs)
...
self.setup(request, *args, **kwargs)
...
return self.dispatch(request, *args, **kwargs)
首先实例化了类自己 cls(),并赋值给 self ,也就是你编写的类视图的实例。
接着调用 self.setup() 对实例的属性进行了初始化。setup() 方法非常简单:
def setup(self, request, *args, **kwargs):
"""Initialize attributes shared by all view methods."""
self.request = request
self.args = args
self.kwargs = kwargs把接收的参数原封不动的赋值到类实例中。
这几个属性经常能用到,比如
self.kwargs.get('id')获取url中传递的id值。
view() 函数最后返回了 dispatch() ,它的源码是这样的:
class View:
# dispatch 用到的http请求方法名 -- 杜赛注
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
...
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.
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
dispatch() 非常简短,功能却非常重要:如果 request.method 是一个 GET 请求,则调用类视图 self.get() 方法,如果是 POST 请求,那就调用 self.post() 方法。这就起到根据 http 请求类型派发到不同函数的功能,这是类视图的核心了。
回到 as_view() 来,它最后做了属性赋值、修改函数签名等收尾工作后,返回了 view 函数闭包:
def as_view(cls, **initkwargs):
...
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() 方法就完成了,来总结一下它的核心流程:
as_view()内部定义了view()函数。view()函数对类视图进行初始化,返回并调用了dispatch()方法。dispatch()根据请求类型的不同,调用不同的函数(如get()、post()),并将这些函数的response响应结果返回。as_view()返回了这个view函数闭包,供path()路由调用。
把核心部分拿出来就这样:
class View:
...
@classonlymethod
def as_view(cls, **initkwargs):
...
def view(request, *args, **kwargs):
self = cls(**initkwargs)
...
self.setup(request, *args, **kwargs)
...
return self.dispatch(request, *args, **kwargs)
...
return view
def setup(self, request, *args, **kwargs):
self.request = request
self.args = args
self.kwargs = kwargs
def dispatch(self, request, *args, **kwargs):
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), ...)
...
return handler(request, *args, **kwargs)
结果就是 as_view() 返回了一个函数(携带着必要的参数),和你用视图函数时直接传递给路由一个函数的效果是相同的。





Comments | NOTHING