CBV与FBV的区别/CBV源码剖析

FBV与CBV

# 针对于视图函数(views.py),视图函数编写逻辑既可以使用函数(FBV)也可以使用类(CBV)来编写。

区别展示:

login.html

<h1>GET请求</h1>

<form action="" method="post" enctype="multipart/form-data">
    <p>username:<input type="text" name="username"></p>
    <p>password:<input type="password" name="password"></p>

    <input type="submit">

</form>

FBV

urls.py

urlpatterns = [
    url(r'^loginc/',views.login),
]

views.py

def login(request):
    if request.method == 'POST':
        return HttpResponse('Post请求')
    return render(request,'login.html')

CBV

urls.py

urlpatterns = [
    url(r'^login/',views.Mylogin.as_view())  # 识别类方式
]

views.py

from django.views import View   # 固定模块

class Mylogin(View):   # 继承View
    def get(self,request):   # 定义get方法
        return render(request,'login.html')

    def post(self,request):  # 定义post方法
        return HttpResponse('Post请求')

总结:
FBV与CBV各有千秋

CBV特点:能够直接根据请求方式的不同直接匹配到对应的方法执行

CBV源码剖析

准备工作:做一个简单的CBV来研究路由层(urls.py)到底是怎么触发视图层(views.py)的'类'方法的。

views.py视图层

from django.views import View


class MyLogin(View):
    def get(self,request):
        return render(request,'login.html')  # 收到get请求走这个方法返回一个页面


    def post(self,request):
        return HttpResponse('post请求')   # 收到post请求走这个返回一个'字符串'

login.html

<form action="" method="post">
    <input type="submit">   <!--点击提交按钮触发post请求-->
</form>

urls.py路由层

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'login/',views.MyLogin.as_view())
]
那么我们如何去研究呢?
# 我们先来探讨url
url(r'login/',views.MyLogin.as_view())

# 我们可以看到他与FBV的区别就在于后面所对应的 类名.as_view()

我们知道在python中一切皆'对象',那么类使用(.)的方式时,类(.)的是肯定是一个属性或者一个方法
那么说明as_view()要么是一个属性要么是一个方法。
但是他加了一个(), 我们可猜测他是一个方法,他就相当于'函数名+()'

# 补充: 函数名/方法名 加括号()执行优先级最高,
这就就相当于在用户访问路由后缀为login/时,就会立刻执行views下的MyLogin类下的as_view()
# 那么我们就来研究这个as_view()到底是什么样的逻辑呢。

猜测:as_view()  # 要么是被@staticmethod修饰的静态方法
	 as_view()  # 要么是被@classmethod修饰的类方法
    
# 通过ctrl+左键的方式点进去看一下他的源码

@classonlymethod
def as_view(cls, **initkwargs):
    
# 通过源码我们可以看到他确实是一个绑定给类的方法,类来调用,就把类当作第一个参数传入
我们来研究一个这个函数:

函数的返回值:view  # 为内部函数的函数名
# 那么在启动django的时候就会立刻执行as_view方法,as_view的返回值为view
那么就相当于:
url(r'login/',views.MyLogin.as_view())
同等于:
url(r'^login/,views.view')  # 那么这个结果是不是和FBV模式一摸一样

# 通过这一点:CBV与FBV在路由匹配上本质是一样的。都为(路由,views.函数内存地址)
那么在用户输入路由后缀为login/的时候就会自动触发view方法。

那么我们来研究view方法:

def view(request, *args, **kwargs):
    self = cls(**initkwargs)  # cls为我们自己写的类(MyLogin) 加括号后产生对象
    # 相当于:self = MyLogin(**initkwargs)  # 类加()就产生一个我们自己写的类的对象self
    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)
    # 对象.一个属性 :查找属性的顺序:先去对象自己找,再去产生对象的类里面找,之后再去父类里找
    # 一定要明白当前的self是谁。(这里self为我们自己写的类产生的对象)
    # 那么这里没有dispatch方法 ,我们自己写得类里面也没有,那么就去类继承得父类View查看有没有该方法
    
# 那么我们就去找一下这个dispatch属性/方法

研究:View: dispatch

# 很明显在上述的view方法里没有,我们自己写的MyLogin类里肯定也是没有的,那么我们就来研究视图层:我们所编写的类所继承的(View)
class MyLogin(View):   # 同样通过ctrl+鼠标左键的方式点进去看一下
    

  # 我们来研究一下这个代码 
    
    def dispatch(self, request, *args, **kwargs):
        # 获取当前请求方式并转为小写 然后比对当前比对是否合法(这里就以'GET'请求为例)
        if request.method.lower() in self.http_method_names:
            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        # getattr反射:通过字符串来操作对象的属性或者方法(那么第二个参数位置即为'get')如果'get'方法有值就返回get方法得返回值,我们在定义类得时候,定义了get方法的,所有就会返回get方法得返回值
        # 括号内参数解释:(self:自己写的类产生的对象,'get',当找不到get方法的时候就会找第三个参数)
        # 那么现在就相当于:
            # handler = getattr(我们自己写的类产生的对象,'get','当找不到get属性或者方法就会用第三个参数返回该参数对应方法得返回值')
            # get如果存在:hanler就相当于:handler = 我们自己写的get方法
        else:
            handler = self.http_method_not_allowed  # 找不到get方法走的方法(抛出异常)
        return handler(request, *args, **kwargs)   # 自动调用我们自己写的get方法

    #  那么post方法同理
这样就疏通了CBV的执行流程。其实内部还是FBV
posted @ 2022-03-02 15:11  JasonBorn  阅读(359)  评论(0编辑  收藏  举报