Django源码剖析(06)--view

View类进行一个小小的阅读

1. View

通常如果不使用DRF框架的话, 会使用django.views.generic.base.View来派生出我们自己的View.
来看一下源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
class View(object):
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

def __init__(self, **kwargs):
for key, value in six.iteritems(kwargs):
setattr(self, key, value)

@classonlymethod
def as_view(cls, **initkwargs):
for key in initkwargs:
if key in cls.http_method_names:
raise TypeError("")
if not hasattr(cls, key):
raise TypeError("")

def view(request, *args, **kwargs):
self = cls(**initkwargs)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
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

def dispatch(self, request, *args, **kwargs):
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)

def http_method_not_allowed(self, request, *args, **kwargs):
logger.warning(
'Method Not Allowed (%s): %s', request.method, request.path,
extra={'status_code': 405, 'request': request}
)
return http.HttpResponseNotAllowed(self._allowed_methods())

def options(self, request, *args, **kwargs):
response = http.HttpResponse()
response['Allow'] = ', '.join(self._allowed_methods())
response['Content-Length'] = '0'
return response

def _allowed_methods(self):
return [m.upper() for m in self.http_method_names if hasattr(self, m)]

在干掉了绝大多数的注释和异常之后, 代码只有约55行, 就是这55行代码来完成最重要的视图处理.

2. as_view方法

url中使用时, 我们会主动的调用as_view方法来返回一个函数.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@classonlymethod
def as_view(cls, **initkwargs):
for key in initkwargs:
if key in cls.http_method_names:
raise TypeError("")
if not hasattr(cls, key):
raise TypeError("")

def view(request, *args, **kwargs):
self = cls(**initkwargs)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
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

这是一个闭包函数, 最终返回了view函数的句柄, 从这段代码上还能够看出很多Python特色的特性的.
最开始的不定长具名参数拆包不看了, 一般也用不到, 直接pass.

1
2
view.view_class = cls
view.view_initkwargs = initkwargs

可以很清晰的看出, 函数也是一种对象, 可以进行属性的赋值. update_wrapper为一个内建方法, 保持原有类对象的名称和相关注释. 最终返回了这个函数句柄.
那么调用view(request)会发生什么?
首先, 实例化了自己, 也就是派生的View类; 然后调用自己的dispatch方法.

3. dispatch方法

1
2
3
4
5
6
def dispatch(self, request, *args, **kwargs):
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)

假如我们的请求为get, 那么在getattr方法中尝试获取实例的get属性或者是方法, 然后进行调用, 返回.

没有了, 剩下的就是request请求对象进入, 然后递归的进行url匹配, 匹配到了进行函数调用(也就是view函数), 调用完返回.
整个过程看起来就是这么简单, 但是精髓就在于将一个类转换为一个函数, 函数中仍然能够存储类的相关信息.

闭包: 拥有极强的威力