CBV之Django中View类部分源码分析

CBV—基于类的视图

CBV基本写法:

views.py中的写法:

from django.conf.urls import url
from django.contrib import admin
from django.views import View

class MyLogin(View):  # 这里的MyLogin是我们自定义的CBV类
    def get(self, request):
        return render(request, 'login.html')
    def post(self, request):
        return HttpResponse('我是类里面的post方法')

url写法:

url(r'^login/', views.MyLogin.as_view())

login.html中的代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
    <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<form action="" method="post">
    {% csrf_token %}
    <input type="submit">
</form>
</body>
</html>

启动项目,使用浏览器请求url: 127.0.0.1:8000/login/,显示结果为(此时为GET请求)
CBV之Django中View类部分源码分析

当点击提交按钮后,由login.html代码中可见form表单使用的是POST请求,获得如下结果:

CBV之Django中View类部分源码分析

请求达到django会先执行django中间件里的方法,然后执行路由匹配。

在路由匹配完成后,会执行CBV类中的as_view方法。

但是此时,我们定义的类中并没有as_view方法,所以会接着到它的父类View中寻找并使用父类中的as_view方法。

django中View类的部分源码:

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):  # cls是我们自己写到的MyLogin类 
        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)  # cls是我们自己写到的MyLogin类
            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
 
        update_wrapper(view, cls, updated=())


        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(我们自己写的类产生的对象,'get',报错信息
            # handler就是我们自己类里面的get方法
            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed
        return handler(request, *args, **kwargs)
        # 调用类里面的get方法

从view中的源码可以看出,再View类中,先定义了http请求的八种方法

http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

在【as_view】方法中进行判断,如果请求的方法没在 【http_method_names】中,则会抛出异常,

@classonlymethod
    def as_view(cls, **initkwargs):  # cls是我们自己写到的MyLogin类 

【as_view】方法中又定义了view方法,在view方法中对CBV类进行实例化,并为其设置属性,向浏览器发送request请求。

最后调用【dispatch】方法并返回【dispatch】方法的值来对request进行处理:

由于self对象就是CBV实例化得到,所以会先执行自定义的CBV类中的dispatch方法。如果CBV类中没有定义dispatch方法则执行Django的View中的dispatch方法
上一篇:flask 的cbv


下一篇:CBV到DRF源码分析