视图:FBV与CBV

FBV

FBV(function base views)
就是在视图里使用函数处理请求。
之前都是FBV模式写的代码,所以就不写例子了。

CBV(class base views)

就是在视图里使用类处理请求。
Python是一个面向对象的编程语言,如果只用函数来开发,有很多面向对象的优点就错失了(继承、封装、多态)。
所以Django在后来加入了Class-Based-View。可以让我们用类写View。
这样做的优点主要下面两种:

提高了代码的复用性,可以使用面向对象的技术,比如Mixin(多继承)
可以用不同的函数针对不同的HTTP方法处理,而不是通过很多if判断,提高代码可读性

写法

如果我们要写一个处理GET方法的view

用函数写的话是下面这样:

#路由中:
url('^now$',views.count_now)
#视图函数中:
def count_now(request):
    now_time = time.localtime()
    now_str = time.strftime('%Y-%m-%d %H:%M:%S',now_time)
    if request.method == 'GET':
        return render(request,'now.html',{'now_str':now_str})

如果用class-based view写的话,是这样:

#路由中:
url('^now$',views.MyNow.as_view())
#视图函数中
from django.views import View
class MyNow(View):
    def get(self,request):
        now_time = time.localtime()
        now_str = time.strftime('%Y-%m-%d %H:%M:%S',now_time)
        return render(request,'now.html',{'now_str':now_str})

添加类的属性

方法一:

Python的方法,可以被子类覆盖

class MyNow(View):

    name = 'wanghw'

    def get(self,request):
        now_time = time.localtime()
        now_str = time.strftime('%Y-%m-%d %H:%M:%S',now_time)
        return render(request,'now.html',{'now_str':now_str,'name':self.name})

方法二:

可以在url中指定类的属性

urlpatterns = [
    path('admin/', admin.site.urls),
    url('^now$',views.MyNow.as_view(name='whw')) #类里面必须有name属性,并且会被传进来的这个属性值给覆盖掉
]
注意:最终name属性的值是url里面传进去的值whw

使用Mixin

要理解django的class-based-view(以下简称cbv),首先要明白django引入cbv的目的是什么。在django1.3之前,generic view也就是所谓的通用视图,使用的是function-based-view(fbv),亦即基于函数的视图。有人认为fbv比cbv更pythonic,其实不然。python的一大重要的特性就是面向对象。而cbv更能体现python的面向对象。cbv是通过class的方式来实现视图方法的。class相对于function,更能利用多态的特定,因此更容易从宏观层面上将项目内的比较通用的功能抽象出来。关于多态,不多解释,有兴趣的同学自己Google。总之可以理解为一个东西具有多种形态(的特性)。cbv的实现原理通过看django的源码就很容易明白,大体就是由url路由到这个cbv之后,通过cbv内部的dispatch方法进行分发,将get请求分发给cbv.get方法处理,将post请求分发给cbv.post方法处理,其他方法类似。怎么利用多态呢?cbv里引入了mixin的概念。Mixin就是写好了的一些基础类,然后通过不同的Mixin组合成为最终想要的类。
所以,理解cbv的基础是,理解Mixin。Django中使用Mixin来重用代码,一个View Class可以继承多个Mixin,但是只能继承一个View(包括View的子类),推荐把View写在最右边,多个Mixin写在左边。

CBV的写法及源码分析步骤

#视图函数的写法
from django.views import View
class LoginView(View): #继承View
    
    #重写dispatch方法的写法
    def dispatch(self,request,*args,**kwargs)
        #执行父类的dispatch方法
        ret = super().dispatch(request,*args,**kwargs)
        return ret  #别忘了return值
        
    def get(self,request): #根据用户的请求方法,找到对应的方法

        return render(request,'login.html')

    def post(self,request):
        print(request.POST)
        uname = request.POST.get('username')
        pwd = request.POST.get('password')
        if uname == 'chao' and pwd == '123':
            return render(request, 'home.html')
        return HttpResponse('用户名或者密码错误')
        
#路由的写法及源码分析
url(r'^login/', views.LoginView.as_view()), 

###分析CBV源码的步骤:
1.as_view()--view,
2.用户请求到了 view,
3.view里面执行了dispatch,反射找到请求方法对应的自己视图的方法

给视图加装饰器

使用装饰器装饰FBV

FBV本身就是一个函数,所以和给普通的函数加装饰器无差

一定要记得,装饰器return func返执行的结果
def wrapper(func):
    def inner(*args,**kwargs):
        print(123)
        ret = func(*args,**kwargs)
        print(456)
        #注意必须return func执行的结果!
        return ret
    return inner

@wrapper
def count_now(request):
    now_time = time.localtime()
    now_str = time.strftime('%Y-%m-%d %H:%M:%S',now_time)
    if request.method == 'GET':
        return render(request,'now.html',{'now_str':now_str})

使用装饰器装饰CBV

类中的方法与独立函数不完全相同,因此不能直接将函数装饰器应用于类中的方法 ,我们需要先将其转换为方法装饰器。
Django中提供了method_decorator装饰器用于将函数装饰器转换为方法装饰器。

我们还用上面的装饰器:

def wrapper(func):
    def inner(*args,**kwargs):
        print(123)
        ret = func(*args,**kwargs)
        print(456)
        #注意必须return func执行的结果!
        return ret
    return inner
注意,先引入method_decorator:
from django.utils.decorators import method_decorator

方式一

给get或post方法单独加:
class MyNow(View):
    name = 'wanghw'
    @method_decorator(wrapper)
    def get(self,request):
        now_time = time.localtime()
        now_str = time.strftime('%Y-%m-%d %H:%M:%S',now_time)
        return render(request,'now.html',{'now_str':now_str,'name':self.name})

方式二

给dispatch方法(分发方法)加~这样的话相当于给get或post方法都加上了装饰器
class MyNow(View):
    name = 'wanghw'

    @method_decorator(wrapper)
    def dispatch(self, request, *args, **kwargs):
        print('before')
        obj = super().dispatch(request,*args,**kwargs)
        print('after')
        #注意要return obj
        return obj

    def get(self,request):
        now_time = time.localtime()
        now_str = time.strftime('%Y-%m-%d %H:%M:%S',now_time)
        return render(request,'now.html',{'now_str':now_str,'name':self.name})

方式三

使用“装饰器嵌套”给类加——因为name后面的值必须是个字符串:
装饰器叠加:内层装饰器的返回值给了外层了~
@method_decorator(wrapper,name='get')
@method_decorator(wrapper,name='post')
class MyNow(View):
    name = 'wanghw'

    def get(self,request):
        now_time = time.localtime()
        now_str = time.strftime('%Y-%m-%d %H:%M:%S',now_time)
        return render(request,'now.html',{'now_str':now_str,'name':self.name})

    def post(self,request):
        pass

用于理解的写法:

from django.utils.decorators import method_decorator

方式三:分别添加
@method_decorator(xxx,name='post')
@method_decorator(xxx,name='get')
class IndexView(View):
方式一:一次性添加
@method_decorator(xxx)
def dispatch(...):....

方式二:分别添加
@method_decorator(xxx)
def get(self,request):pass
def post(self,request):pass

关于CSRF Token相关装饰器在CBV模式下使用的补充

CSRF Token相关装饰器在CBV只能加到dispatch方法上,或者加在视图类上然后name参数指定为dispatch方法!
备注:

csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。

csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。

写法1

from django.views.decorators.csrf import csrf_exempt, csrf_protect
from django.utils.decorators import method_decorator


class HomeView(View):

    @method_decorator(csrf_exempt)
    def dispatch(self, request, *args, **kwargs):
        return super(HomeView, self).dispatch(request, *args, **kwargs)

    def get(self, request):
        return render(request, "home.html")

    def post(self, request):
        print("Home View POST method...")
        return redirect("/index/")

写法2

from django.views.decorators.csrf import csrf_exempt, csrf_protect
from django.utils.decorators import method_decorator


@method_decorator(csrf_exempt, name='dispatch')
class HomeView(View):
   
    def dispatch(self, request, *args, **kwargs):
        return super(HomeView, self).dispatch(request, *args, **kwargs)

    def get(self, request):
        return render(request, "home.html")

    def post(self, request):
        print("Home View POST method...")
        return redirect("/index/")
posted on 2019-05-20 21:37  江湖乄夜雨  阅读(433)  评论(0编辑  收藏  举报