一、app.route与add_url_rule简介
1.在Flask应用中,路由是指用户请求的URL与视图函数之间的映射,处理URL与函数之间关系的程序称为路由。HTTP请求的URL,Flask框架根据在路由表中匹配预定义的URL规则,找到对应的视图函数,并将视图函数的执行结果返回给服务器。
通常URL组成如下:
URL=传输协议+主机号+端口号+(目录)文件名; 传输协议一般是http或者https,主机号主要指服务(www)+域名。
1)url_for(): 以视图函数名为参数,返回对应的URL;
2) app.route: 将视图函数和URL绑定;
@app.route('/',endpoint='index')
def hello_world():
return "hello world"
上述代码实现了将URL'/' 与视图函数hello_world()的绑定,因为使用了endpoint参数,在使用url反转时就不能使用视图函数名,而是要用我们定义的URL名,即 url_for('index') 。
3)add_url_rule:也是将视图函数和URL绑定;
其中参数 rule:设置的URL
endpoint: 给URL设置的名称
view_func:指定视图函数的名称
from flask import Flask, render_template, request, redirect, url_for
app =Flask(__name__)
@app.route('/', methods=['POST', 'GET'],endpoint='index')
def hello_world():
return "hello world!"
def my_test():
return "my test!"
app.add_url_rule(rule='/test/',endpoint='test',view_func=my_test)
with app.test_request_context():
print(url_for('test'))
if __name__ == '__main__':
app.run( debug=True)
在上述代码中,视图函数为hello_world()和my_test(),
app.add_url_rule(rule='/test/',endpoint='test',view_func=my_test) #定义了路由、endpoint
with app.test_request_context(): #构建了一个虚拟的请求上下文环境
注:1)在浏览器地址栏中直接输入地址是属于GET方法访问
2)Flask是通过endpoint找到视图函数的
二、Flask类视图
前面接触的视图都是函数,所以一般简称为视图函数;视图函数也可以基于类来实现,类视图的好处是支持继承,编写完类视图需要通过 app.add_url_rule(url_rule,view_func)来进行注册(类似java里面的抽象类)。Flask类视图一般分为标准类视图和基于调度方法的类视图。
2.1、标准类视图
1)标准类视图特点:
必须继承flask.views.View。(父类中继承)
必须实现dispatch_request方法,以后请求过来后,都会执行这个方法,这个方法的返回值相当于之前的视图函数。(在子类中使用)
必须通过 app.add_url_rule(rule,endpoint,view_func)来做URL与视图的映射,view_func参数需要使用as_view类方法转换。
如果指定了endpoint,那么在使用url_for反转时必须使用endpoint指定的值。
from flask import Flask, render_template, request, views
app =Flask(__name__)
class Ads(views.View): #定义视图类Ads
def __init__(self): #实例化
super().__init__() #继承自__init__()方法
self.context ={ #设置
'ads': "这是一个视图类例子!"
}
class Index(Ads): #定义Index类,继承Ads
def dispatch_request(self): #使用dispatch_request()方法,所定义类视图
return render_template('index.html',**self.context) #渲染模板
class Login(Ads):
def dispatch_request(self): # V使用dispatch_request()方法,所定义类视图
return render_template('login.html', **self.context) # 渲染模板
class Register(Ads):
def dispatch_request(self): # 使用dispatch_request()方法,所定义类视图
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)
该代码中,定义了一个视图函数Ads(),该函数继承自flask.views.View, 在该函数中,返回一个元组作为Response对象;
def __init__(self): 是一个初始化方法,__init__方法的第一个参数永远是self,表示创建的示例本身,可以在该方法的内部,将各种属性绑定到self,因为self就指向创建的示例本身;
其中,一个index.html如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
首页!{{ads}}
</body>
</html>
以下为实验结果
2.2、基于方法的类视图
利用视图函数实现不同的请求执行不同的逻辑时比较复杂,需要在视图函数中进行判断,如果利用方法视图实现就比较简单。Flask提供另外一种类视图flask.views.MenthodView,对每个HTTP方法执行不同的函数。
from flask import Flask, render_template, request, views
app =Flask(__name__)
@app.route('/',methods=['get','post'])
def hello_world():
return render_template('index.html')
class LoginView(views.MethodView): #定义视图类Ads
def get(self):
return render_template('index.html')
def post(self):
username=request.form.get("username")
password=request.form.get('pwd')
if username=='admin' and password=='admin':
return "correct!"
else :
return "error"
app.add_url_rule(rule='/login',view_func=LoginView.as_view('loginview')) #注意rule='/login',后面没有“/”
if __name__ == '__main__':
app.run( debug=True)
注意:这里rule='/login' , 而不是rule= '/login/' , 如果写成后者会出错,估计是这里用到了提交表单,只能要求前者的写法。
<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>
三、Flask装饰器
装饰器本质上是一个python函数,可以让其它函数在不需要做任何代码变动的前提下增加额外的功能,装饰器的返回值是一个函数对象。装饰器经常用于有切面需求的场景。比如插入日志、性能测试、事务处理、缓存和权限校验等场景。
装饰器是解决这类场景的绝佳设计,有了装饰器,可以抽离大量与函数功能无关的雷同代码继续重用。
1、装饰器的定义和基本使用
1)例如场景:一个新闻站点,新闻列表页、详情页等都要求用户登录才能够浏览。下面定义的user_login()函数,实质就是一个装饰器。
def user_login(func): #定义函数,使用func接收函数作为参数
def inner():#定义inner()函数
print('登录操作')#打印输出
func()#执行func函数
return inner#返回inner函数,不是返回函数的结果
user_login(func): 使用func接收函数作为参数 ,定义了一个inner()函数
2)新闻页函数,要求登录后才能访问
from flask import Flask, render_template, request, redirect, url_for
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来执行
if __name__ == '__main__':
app.run()
其中,news() : 先执行inner()函数,再执行func()函数,即为new()函数
四、蓝图
随着业务代码的增加,将所有代码都放在单个程序文件中是非常不合适的。flask蓝图提供了模块化管理程序路由的功能,使程序结构清晰、简单易懂。
一个程序执行文件中,如果功能代码过多,是不方便后期维护的。
蓝图的定义:在蓝图被注册到应用后,所要执行的操作的集合。当分配请求时,Flask会把蓝图和视图函数关联起来,并生成两个端点之前的URL 。
主路由视图函数app.py:
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文件:
from flask import Blueprint#导入Blueprint模块
new_list = Blueprint('news', __name__) # 创建一个blueprint对象。第一个参数可看做该blueprint对象的姓名
# 在一个app里,姓名不能与其余的Blueprint对象姓名重复
# 第二个参数__name__用作初始化
@new_list.route("/news") # 将蓝图对象当做‘app’那样使用
def new():#定义函数news()
return '这是新闻模块!'
products.py文件:
from flask import Blueprint#导入Blueprint模块
product_list = Blueprint('products', __name__) # 创建一个blueprint对象。第一个参数可看做该blueprint对象的名字
# 在一个app里,对象名不能与其余的Blueprint对象名重复
# 第二个参数__name__用作初始化
@product_list.route("/products") # 将蓝图对象当做‘app’那样使用
def product():
return '这是产品模块!'
视图函数的名称不能和蓝图对象的名称相同。蓝图的目的是实现各个模块的视图函数写在不同的py文件中。再主视图中导入分路由视图的模块,并且注册蓝图对象。