Django有两种视图模式:
- FBV(Function base Views)
- CBV(Class base Views)
FBV很容易理解,就是一个路由对应了一个函数。今天重点讲下CBV。采用CBV模式开发接口的好处是什么?在这之前我们先举个例子,比如说我做自动化测试平台,有测试用例这么一个概念,我用一张表存测试用例的信息,包括用例标题、执行步骤、创建人、创建时间等等。那么我写的接口一般有以下几个:
- 创建测试用例的接口
- 删除测试用例的接口
- 查询测试用例的接口
- 编辑测试用例的接口
也就是我们常说的增删查改。如果我采用FBV视图模式来开发,那么我就要写四个Function:
def create_testcase(request): pass def delete_testcase(request): pass def get_testcase(request): pass def modify_testcase(request): pass
在每个函数中我还要判断一下请求方法。
并且urls.py中要配置4个路由分别指向这四个Function,当操作的函数越来越多时,路由文件将会越来越大。
这时候CBV模式的好处就体现出来了。
- 减少路由配置
- 自动判断请求方法,调用对应的函数
- 可以实现多继承,减少代码,提高代码复用性
- 把一个对象的增删查改写在一个类里,便于管理维护
采用CBV,我们可以把上述的增删查改写在一个视图当中
class TestCaseViews(View):
def post(self):
"""对应新增"""
pass
def delete(self):
"""对应删除"""
pass
def get(self):
"""对应查询"""
pass
def put(self):
"""对应修改"""
pass
urls.py路由可以这么配置
path(r'^testcase', TestCaseViews.as_view())
那么,当你用get请求方法请求 http:/xxxx/testcase时,路由会自动调用get方法来处理请求;当你用post请求方法请求 http:/xxxx/testcase时,路由会自动调用post方法来处理请求。
二、CBV源码解析CBV模式下,路由是怎么根据不同的请求方法来决定调用哪个方法的呢?
显而易见,CBV和FBV不同,它在配置路由时调用了一个as_view()方法,蹊跷肯定就在这里,让我点进去看一下:
class View:
"""
Intentionally simple parent class for all views. only implements
dispatch-by-method and simple sanity checking.
"""
# 这里枚举了http请求的所有方法
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
def __init__(self, **kwargs):
"""
Constructor. Called in the URLconf; can contain helpful extra
keyword arguments, and other things.
"""
# Go through keyword arguments, and either save their values to our
# instance, or raise an error.
# 这里的操作是给自身对象赋值,**kwargs参数是不限个数的键值对参数的意思,kwargs
for key, value in kwargs.items():
setattr(self, key, value)
# 这个就是在路由配置中调用的方法
@classonlymethod
def as_view(cls, **initkwargs):
"""Main entry point for a request-response process."""
# 这里是遍历as_view()传入的键值对参数
for key in initkwargs:
# 如果key是http请求的方法名,就抛异常,比如get就不行
if key in cls.http_method_names:
raise TypeError(
'The method name %s is not accepted as a keyword argument '
'to %s().' % (key, cls.__name__)
)
# 这一步就是判断自身View类有没有这个属性
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))
# 定义view方法
def view(request, *args, **kwargs):
self = cls(**initkwargs)
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
def setup(self, request, *args, **kwargs):
"""Initialize attributes shared by all view methods."""
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
# 这个方法就是关键
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:
# 加入我用get方法请求,那这一句的意思是,获取自身对象的'get'方法(这个方法就是
# 你在视图类中定义的get方法),如果没有找到就默认返回http_method_not_allowed方法
# 这个其实就是利用了Python的反射机制来查找出需要执行的方法。
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 HttpResponseNotAllowed(self._allowed_methods())
def options(self, request, *args, **kwargs):
"""Handle responding to requests for the OPTIONS HTTP verb."""
response = 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)]
一句话总结,CBV模式是基于Python的反射机制来根据请求方法确定执行对应的函数的。反射机制的关键方法是getattr()。
说到反射机制,这里举个简单例子帮助理解:
class Test:
def get(self):
print("调用了get方法")
def post(self):
print("调用了post方法")
def delete(self):
print("调用了delete方法")
def put(self):
print("调用了put方法")
test = Test()
handle = getattr(test, 'get')
handle()



