视图: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/")