第四章 Flask视图高级技术
目录
2.请用装饰器相关知识,编程实现统计一段程序中多个函数各自执行所花时间。
如何自己定义函数?如何定义和使用装饰器?如国功能代码过多,如何实现程序的模块化编程?
本章围绕这些问题,主要介绍路由函数、Flask类视图、基于方法的类视图、装饰器、蓝图的概念和装饰器。
本章涉及到的知识点有:
-
路由函数app.route和add_url_rule函数的使用;
-
标准类视图的基本使用;
-
基于方法的类视图的基本使用;
-
装饰器的基本使用,以及带参数的函数装饰器的方法;
-
蓝图的定义、作用及应用。
4.1 app.route与add_url_rule简介
app.route('/')定义路由
app.add_url_rule('rule=/test/',endpoint='my_test',view_func=my_test)
-
rule:设定的URL (可省略)
-
endpoint:给UPL设置的名称
-
view_func:指定视图函数的名称。
from flask import Flask
app=Flask(__name__)
@app.route('/')
def hello_world():
return "Hello World"
def my_test():
return "这是测试界面"
app.add_url_rule(rule='/test/',endpoint='my_test',view_func=my_test)
if __name__ == '__main__':
app.run()
4.2 Flask类视图
之前我们接触的视图都是函数,所以一般简称为视图函数。其实视图函数也可以基于类来实现,类视图的好处是支持继承,
编写类函数需要通过app.add_url_rule(url_rule,view_func)来进行注册。
Flask类视图一般分为标准视图和基于调度方法的类视图。
4.2.1标准类视图(不太理解)
app.py文件
from flask import Flask,render_template,views
app = Flask(__name__)
class Ads(views.View):
def __init__(self):
super().__init__()
self.context = {
'ads': '这是对联广告!'
}
class Index(Ads):
def dispatch_request(self):
return render_template('index.html', **self.context)
class Login(Ads):
def dispatch_request(self):
return render_template('login.html', **self.context)
class Register(Ads):
def dispatch_request(self):
return render_template('register.html', **self.context)
app.add_url_rule(rule='/', endpoint='index', view_func=Index.as_view('Index'))
app.add_url_rule(rule='/login/', endpoint='login', view_func=Login.as_view('login'))
app.add_url_rule(rule='/register/', endpoint='register', view_func=Register.as_view('register'))
if __name__ == '__main__':
app.run(debug=True)
在app.py文件的03~08行定义了一个视图函数Ads(),该函数继承自flask.views.View,在该函数中,我们返回一个元祖作为Response对象。 04行是一个初始化方法,init方法的第一个参数永远是self,表示创建的实例本身,因此,在init方法内部就可以把各种属性绑定到self,因为self就指向创建的实例本身。 09行表示定义Index类,继承自Ads; 10行表示使用dispatch_reuqest()方法,定义类视图;11行表示渲染模板;18~20行添加路由,指定endpoint和对应的视图函数。
inde.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
这是首页!{{ ads }}
</body>
</html>
4.2.2 基于方法的类视图
<form>标签语法格式
在html中,<form>标签是使用来创建供用户输入的html表单,在网页中很常见,比如:注册和登录页面就是用表单实现的。
<form>标签属性
action:当提交表单时向何处发送表单数据;
method:用于发送表单数据的HTTP方法,值可以是:get、post;
<form action="login" method = "post"><!--表单开始-->
app.py
#encoding:utf-8
from flask import Flask,render_template,request,views#导入相应模块
app = Flask(__name__)#Flask初始化
@app.route('/')#定义路由
def hello_world():#定义视图函数
return render_template('index.html')#渲染模板
class LoginView(views.MethodView):#定义LoginView类
# 当用户通过get方法进行访问的时候执行get方法
def get(self):#定义get函数
return render_template("index.html")#渲染模板
# 当用户通过post方法进行访问的时候执行post方法
def post(self):#定义post 函数
username = request.form.get("username")#接收表单中传递过来的用户名
password = request.form.get("pwd")#接收表单中传递过来的密码
if username == 'admin' and password == 'admin':#如果用户名和密码是否为admin
return "用户名正确,可以登录!"#i f语句为真的话,返回可以登录信息
else:
return "用户名或密码错误,不可以登录!"#否则,返回不可以登录信息
# 通过add_url_rule添加类视图和url的映射关系
app.add_url_rule('/login',view_func=LoginView.as_view('loginview'))
if __name__ == '__main__': #当模块被直接运行时,代码将被运行,当模块是被导入时,代码不被执行
app.run(debug=True)#开启调试模式
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"><!--设定网页编码-->
<title>Title</title><!--设定网页标题-->
<style type="text/css"><!--自定义CSS-->
.div1 {<!--定义div1容器-->
height:180px; <!--高度为180px-->
width:380px; <!--宽度为380px-->
border:1px solid #8A8989; <!--边框实线-->
margin:0 auto; <!--使得元素水平对齐-->
}
.input{<!--定义容器input-->
display: block; <!--让对象成为块级元素-->
width: 350px; <!--宽度为350px-->
height: 40px; <!--高度为40px-->
margin: 10px auto; <!--使元素水平对齐-->
}
.button<!--定义容器button-->
{
background: #2066C5; <!--背景颜色-->
color: white; <!--设置字体颜色-->
font-size: 18px; <!--设置字体大小-->
font-weight: bold; <!--字体加粗-->
height: 50px; <!--容器高度为50px-->
border-radius: 4px; <!--所有角都使用半径为4px的圆角-->
}
</style>
</head><!--head区域完毕-->
<body><!--body区域开始-->
<div class="div1">
<form action="login" method = "post"><!--表单开始-->
<input type="text" class="input" name="username" placeholder="请输入用户名">
<input type="password" class="input" name="pwd" placeholder="请输入密码">
<input type="submit" value="登录" class="input button"><!--定义登录submit-->
</form></div><!--表单定义完毕-->
</body>
</html>
4.3 Flask装饰器
装饰器本质上是一个Python函数,他可以让其他函数在不需要做任何代码改动的前提下增加额外的功能
装饰器的返回值也是一个函数对象。装饰器经常用于有切面需求的场景,
比如插入日历、性能测试、事务处理、缓存和限制效验等场景
有了装饰器,可以抽离出大量与函数无关的雷同代码继续使用。
4.3.1装饰器的定义和基本使用
from flask import Flask#导入Flask模块
app = Flask(__name__) #Flask初始化
@app.route('/')#定义路由
def hello_world():#定义函数
return 'Hello World!'#返回值
def user_login(func): #定义函数,使用func接收函数作为参数
def inner():#定义inner()函数
print('登录操作')#打印输出
func()#执行func函数
return inner#返回inner函数,不是返回函数的结果
@user_login#使用了装饰器
def news():#定义函数news
print('这是新闻详情页')#打印输出
news();#调用news来执行
def news():
print("这是新闻详情页面!")
show_nows=user_login(news)
show_nows()
4.3.2 对带参数的函数使用装饰器
有时候给函数加装饰器的时候,这个函数需要传递参数的,那么就涉及对带参函数使用装饰器的问题。
1.函数的可变参数
def func(*args,**kwargs):
-
*args 或 * :代表元组,长度不限
-
**kwargs 或 * *:代表键值对,个数不限
def func(*arge,**keargs): print(len(arge)) print(arge) print(keargs) for i in keargs: print(keargs[i]) func(1,'a',4,username='shishu',scotr=98)
2.对带参函数的函数使用装
以上代码存在一些问题,在调用过程中会改变原来的名称,不管是new()函数还是new_list()函数
最终执行时被替换成innner()函数。为避免出现此种情况,
可以使用functools.wraps在装饰器的函数上对传进来的函数进行包装,这样就不会丢掉原始数据。
from flask import Flask#导入Flask模块
from functools import wraps#导入相应模块
app = Flask(__name__)#Flask初始化
@app.route('/')#定义路由
def hello_world():#定义视图函数
return 'Hello World!'#返回值
def user_login(func):#定义函数user_login
@wraps(func) # 使用functools.wraps在装饰器的函数上,把传进来的函数进行包裹
def inner(*args,**kwargs):#定义内部函数inner
print('登录操作!')#打印输出
func(*args,**kwargs)
return inner#返回inner
@user_login#使用装饰器
def news():#定义函数news()
print(news.__name__)#打印输出此时的函数名称
print('这是新闻详情页!')#打印输出
news();
@user_login#使用装饰器
def news_list(*args):#定义函数news_list
page=args[0]#元祖args[0]赋值给page
print(news_list.__name__)#打印输出函数名
print('这是新闻列表页的第'+str(page)+'页!')#打印输出
news_list(5)#调用函数news_list
if __name__ == '__main__': #当模块被直接运行时,代码将被运行,当模块是被导入时,代码不被执行
app.run()
4.4蓝图
随着业务代码的增加,将将所有代码放在的那个程序中是非常不合适的。这不仅会让阅读代码变得困难,
而且给后期维护带来麻烦。Flask蓝图提供了模块化管理路由的功能,使程序结构清晰、简单易懂。
一个程序执行文件中,如果功能代码过多,是不方便后期维护的。如何实现程序代码模块化,根据具体不同功能模块的实现
划分不同的分类,降低各功能模块之间的耦合度呢?这时候flask.Blunprint(蓝图)就派上用场了。
蓝图定义:在蓝图被注册到应用之后,所要执行的操作的集合。当分配请求时,flask会吧蓝图和视图函数关联起来,
并生成两个端点之前的UPL。
app.py
# -*- coding:utf-8 -*-
from flask import Flask # 导入Flask模块
import news,products#导入相应模块
app = Flask(__name__) # 创建 Flask()对象: app
@app.route('/') # 使用了蓝图,app.route() 这种模式就仍可以使用,注意路由重复的问题
def hello_world():#定义函数
return 'hello my world !'#返回值
app.register_blueprint(news.new_list) # 将news模块里的蓝图对象new_list注册到app
app.register_blueprint(products.product_list) # 将products模块里的蓝图对象product_list注册到app
if __name__ == '__main__':
app.run(debug=True) # 调试模式开 启动服务器 运行在默认的5000端口
news.py
# -*- coding:utf-8 -*-
from flask import Blueprint#导入Blueprint模块
new_list = Blueprint('news', __name__) # 创建一个blueprint对象。第一个参数可看做该blueprint对象的姓名
# 在一个app里,姓名不能与其余的Blueprint对象姓名重复
# 第二个参数__name__用作初始化
@new_list.route("/news") # 将蓝图对象当做‘app’那样使用
def new():#定义函数news()
return '这是新闻模块!'
4.5 练习
1.请在路由中指定请求方法为GET和POST方法。
app.py
from flask import Flask,render_template,request
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
@app.route('/login/',methods=['GET','POST'])
def login():
if request.method=='GET':
return render_template('index.html')
else:
print("这是POST请求!")
if __name__ == '__main__':
app.run()
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style type="text/css">
.div1{
width:350px;
height:300px;
border:1px solid #CC0000;
margin: 0 auto;
}
</style>
</head>
<body>
<div class="div1">
<form action="">
<p>姓名:<input type="text" name="fname" /></p>
</form>
</div>
</body>
</html>
2.请用装饰器相关知识,编程实现统计一段程序中多个函数各自执行所花时间。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style type="text/css">
.div1{
width:350px;
height:300px;
border:1px solid #CC0000;
margin: 0 auto;
}
</style>
</head>
<body>
<div class="div1">
<form action="">
<p>姓名:<input type="text" name="fname" /></p>
</form>
</div>
</body>
</html>