栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

CBV模式源码解析

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

CBV模式源码解析

一、FBV和CBV

Django有两种视图模式:

  1. FBV(Function base Views)
  2. CBV(Class base Views)

FBV很容易理解,就是一个路由对应了一个函数。今天重点讲下CBV。采用CBV模式开发接口的好处是什么?在这之前我们先举个例子,比如说我做自动化测试平台,有测试用例这么一个概念,我用一张表存测试用例的信息,包括用例标题、执行步骤、创建人、创建时间等等。那么我写的接口一般有以下几个:

  1. 创建测试用例的接口
  2. 删除测试用例的接口
  3. 查询测试用例的接口
  4. 编辑测试用例的接口

也就是我们常说的增删查改。如果我采用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模式的好处就体现出来了。

  1. 减少路由配置
  2. 自动判断请求方法,调用对应的函数
  3. 可以实现多继承,减少代码,提高代码复用性
  4. 把一个对象的增删查改写在一个类里,便于管理维护

采用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()

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/273400.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号