[TOC]
03 Django视图层-FBV与CBV
一 引入
官网地址:https://docs.djangoproject.com/en/3.0/topics/class-based-views/
视图是可调用的,用来处理请求(request)并且返回响应(response),django的视图有两种形式:FBV和CBV
1
在早期,人们意识到视图开发的过程中存在一些常见的语法和模式,于是引入基于函数的通用视图来抽象这些模式,并简化了常见情况下的视图开发。因此,刚开始的时候只有FBV,django所做的事情就是向你定义的视图函数传递一个HttpRequest,并且希望返回一个HttpResponse。
随着进一步的发展,人们发现,基于函数的通用视图是有问题的,问题在于它很好地覆盖了简单的情况,但针对稍微复杂的场景,它没有办法在某些配置项之外进行扩展或自定义,从而极大地限制了它在许多实际应用程序中的实用性。而考虑到扩展性与自定义,这正是面向对象技术的强大之处,于是诞生了CBV。
基于类的通用视图与基于函数的通用视图目的都一样,都是为了让视图开发更加容易。但是CBV的解决方案是通过使用mixins,并且django内置了一系列通用视图作为工具集提供给我们继承使用,从而使得基于类的通用视图比基于函数的通用视图更具扩展性和灵活性。
如果你之前尝试过FBV,但发现它有缺陷,CBV是一种新的解决方案,而不是单纯地换了一种定义视图的形式,如果CBV只是视图的另外一种定义形式而已,那岂不是很无聊,连介绍的必要都没有了。
为了让读者快速掌握CBV的使用,我们只能从简单情况下举例,这看起来会是增加了代码的复杂度,读者可能会对其嗤之以鼻,其实不然,在读者日后遇到复杂的情况时,唯有CBV可以进行更高级的设计
综上,CBV并非是FBV的完全替代品,但相对于FBV,CBV确实有一些差异和优势,详解下一小节
1
二 使用CBV
1、CBV允许我们使用不同的实例方法来响应不同的HTTP请求方法,而不是像FBV那样使用条件分支代码
1.1 FBV中视图函数处理HTTP的GET请求如下
from
FBV关于url的配置略
1.2 CBV的实现如下
from
所有基于类的视图都必须继承View类,该类用来处理视图关联到URL的操作,具体分析如下:
由于django的URL解析器期望发送request以及相关参数给一个可调用的函数,而不是一个类,所以基于类的视图有一个as_view()类方法(该方法继承自父类View),调用该方法会返回URL解析器所期望的函数,该函数会在请求到达与关联模式匹配的URL时调用,就像调用视图函数一个样子。查看源码会发现调用该函数首先会创建一个MyView类的实例,然后
1
urls.py的配置如下
# urls.py
虽然最精简的CBV不需要设置任何类属性就可以使用,但类属性确实在血多基于CBV的设计中非常有用,我们又两种方法可以配置或设置类属性
方法一:python标准的套路,在子类中重写/覆盖父类的属性和方法
from
方法二:在URL配置里为as_view()方法传递关键字参数来设置类属性
urlpatterns
2、使用mixins
Mixins机制指的是子类混合(mixin)不同类的属性和功能,比如在django内置的通用视图里有一个mixin类叫TemplateResponseMixin,它的主要目的就是来定义一个方法render_to_response()。当该类和View类组合到一起时,就得到了TemplateView类,TemplateView类分发请求到相应的方法上(通过执行继承自View类的dispath()方法),并且使用render_to_response()方法来找到template_name属性读取指定的模板从而返回一个TemplateResponse对象(通过执行继承自TemplateResponseMixin类的render_to_response()方法)
from
这就极大利用了面向对象重之多继承技术来重用代码的优点,但是鱼与熊掌不可兼得,需要付出的代价就是,在多继承背景下,我们定义的mixin类越多,代码的可读性越差。
关于mixins的用法详见官网https://docs.djangoproject.com/en/3.0/topics/class-based-views/mixins/,篇幅问题此处不再累述。
需要强调的是,CBV可以继承多个mixins类(推荐写在左边),但是只能继承一个父类,该父类只能是View类或者其子/子孙类(推荐写在右面),同时继承两个父类将会导致你的子类无法按照预期那样工作。
三 django内置的CBV
django内置了基本的CBV可以满足相当多的应用,所以的视图都继承自View类,View类负责处理将视图链接到url、HTTP方法调度和其他常见功能。内置的CBV如RedirectView提供了一个HTTP重定向,TemplateView扩展了基类,使其同时可以渲染一个模板。
from
3.1 最简单的使用
以TemplateView为例来介绍内置CBV的使用。最直接使用方式就是直接在URL配置中创建,如果只想改变该CBV的几个属性,你可以为as_view传递参数,如下
from
一样的道理,如果我们想要重定向一个地址,可以设置RedirectView的url属性
from
3.2 继承内置CBV进行重写
第二种更有效的使用内置CBV的方式就是是从现有视图继承并重写子类中的属性(例如template_name)或方法(例如get_context_data),以提供新的值或方法。
例如,我们的需求是要定制一个视图,该视图专门用来显示/渲染模板about.html。Django有一个内置的CBV即TemplateView可以实现,因此我们可以继承它,并重写模板名称template_name以及用来获取渲染模板数据的方法get_context_data
# views.py
url.py配置如下
from
templates/home.html
<!DOCTYPE html>
更多内置CBV详见:
https://docs.djangoproject.com/en/3.0/topics/class-based-views/generic-display/
四 使用CBV处理表单
请看完后续章节中关于表单的使用之后再来读本小节
FBV处理表单代码如下
from
CBV处理表单代码如下
from
上述只是一个很想的例子,但是我们可以定制这个视图,比如覆盖任意的类属性(例如form_class),或者为as_view()传参,或者继承MyForView并重写期中方法。
更多表单处理详见:
https://docs.djangoproject.com/en/3.0/topics/class-based-views/generic-editing/
五 为CBV添加装饰器
之前我们介绍过,若想要为CBV添加额外的功能,可以采用mixins机制,但除此之外,还可以通过添加装饰器的方法实现。有两个地方可以添加装饰
5.1 装饰as_view()方法
# 导入内置的装饰器
示例:
views.py如下
# views.py
urls.py如下
```python from django.urls import path from app01.views import *
urlpatterns = [ path('login/',timer(LoginView.as_view())), ] ```
login.html如下
<!DOCTYPE html>
装饰as_views()相当于给视图类的中的所有方法(例如get、post)都加上了装饰器
5.2 装饰视图类
类方法与独立函数不完全相同,所以你不能简单地把函数装饰器加到类方法上,这就用到了method_decorator方法,该方法会将函数装饰器转换为方法装饰器,然后才可以用来装饰类方法,例如:
装饰dispatch方法,基于CBV,当请求过来后会先执行dispatch()这个方法进而分发到对用的get、post方法上,所以如果需要批量装饰处理请求的方法(如get,post等)可以为dispatch方法添加装饰,这与为as_view()添加装饰器效果时一样的,如下
# views.py
ps:url配置中正常调用即可,无需重复装饰,下同
from
我们当然可以单独装饰不同的方法,如
......
更简洁一点,我们可以把装饰器加在类上,通过name参数指定要装饰的方法,如下
# @method_decorator(timer,name='dispatch') # 批量添加
如果有多个装饰器需要添加,可以定义一个列表或者元组将其存放起来,然后按照下述方式指定
from
上述装饰器会按照列表规定的顺序依次执行装饰器,即先执行deco1,然后deco2。。。与下述添加方式是等同的
......
六 支持其他的HTTP方法
假设我们写了一个图书管理系统,如果有人想使用视图作为API通过HTTP协议访问我们的图书库。API客户端想要获取最新的图书数据,需要时不时地发起连接并下载自上次访问以来新出版的图书数据。但是如果自上次访问完后,就没有新的图书出版,那么从数据库查询图书信息,渲染一个完整的reponse并发送给客户端将白白耗费CPU时间和带宽,此时,我们最好的方式,就是在得知有新书出版时才发起一次请求完整数据的的API调用,这就用到了head方法来查询是否有数据更新
urls.py如下
from
views.py
from
models.py
from
settings.py
TIME_ZONE
编写脚本进行测试
import