文章目录
4.1 app.route与app_url_rule简介
在Flask应用中,路由是指用户请求的URL与视图函数之间的映射,处理URL和函数之间关系的程序称为路由。Flask框架根据HTTP请求的URL在路由表匹配预定义的URL规则,找到对应的视图函数,并将视图函数的执行结果返回给服务器。
4.1.1 app.route的使用
在Flask框架中,默认是使用@app.route
装饰器(装饰器只是一种接收函数的函数,并返回一个新的函数),将视图函数和url绑定如下:
@app.route('/')
def hello_world():
return 'hello world'
上述代码中,视图函数hello_world()
,使用app.route
装饰器会将URL和执行的视图函数的关系保存到app.url_map
属性上。
实际上我们可以给这个装饰器加上一个参数:endpoint
参数(给这个URL命名)
@app.route('/demo', endpoint='index')
def hello_world2():
return 'hello world2'
@app.route('/demo2', endpoint='index2')
def hello_world3():
# url_for : 最简单的用法是以视图函数名,作为参数,返回对应的URL。
# 顾名思义: 返回我们定义的URL名(index)的路由
return url_for('index')
一旦我们使用了endpoint
参数,在使用url_for()
反转时就不能使用视图函数名了,而是要用我们定义的URL名。
4.1.2 add_url_rule的使用
除了使用@app.route
装饰器,我们还可以使用add_url_rule
来绑定视图函数和URL
# @time:2020/11/8 8:57
# Author:Small-J
# add_url_rule:该参数是用来绑定视图函数和URL的
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'hello world'
def my_test():
return '这是测试页面'
# endpoint: URL对应的名字
# view_func: 视图的函数名称,不需要使用括号,使用括号代表着调用的意思
# add_url_rule : 添加路由规则
app.add_url_rule('/test', endpoint='my_test', view_func=my_test)
if __name__ == '__main__':
app.run(debug=True)
add_url_rule源码如下:
rule
: 设置的URL- endpoint : 给URL设置的名称
- view_func : 指定视图函数的名称
总结:
- Flask是通过
endpoint
找到viewfunction(视图函数)的 app.route()
该路由底层也是通过add_url_rule
来使用的
4.2 Flask类视图
之前我们接触的视图都是函数,所以一般简称为视图函数。其实视图函数也可以基于类来实现,类视图的好处是支持继承,编写完类视图需要通过app.add_url_rule(url_rule, view_func)
来进行主持,Flask类视图一般分为标准类视图和基于调度方法的类视图。
4.2.1 标准类视图
标准类视图的特点:
- 必须继承flask.views.View
- 必须实现dispatch_request方法,以后请求过来后,都会执行这个方法,这个方法的返回值相当于之前的视图函数,也必须返回
Response
或者子类的对象,或者是字符串、元组 - 必须通过app.add_url_rule(rule,endpoint, view_func)来做URL与视图的映射,view_func参数需要使用as_view类方法转换。
- 如果指定了endpoint,那么在使用url_for反转时就必须使用endpoint指定的那个指,如果没有指定那么就可以使用as_view(视图名称)中指定的函数名称来作为反转。
- 注意:使用类视图的好处是支持继承,可以把一些共性的东西放在父类中,其他子类可以继承,但是类视图不能跟函数视图一样,写完类视图还需要通过app.add_url_rule(url_rule, view_func)进行注册。
index4.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p>这是首页!{{ abs }}</p>
</body>
</html>
# @time:2020/11/8 9:32
# Author:Small-J
from flask import Flask, views, render_template, url_for
app = Flask(__name__)
# 继承类视图
class Ads(views.View):
def __init__(self):
super().__init__()
self.context = {
'abs': '这是对联广告'
}
class Index(Ads):
# 使用dispatch_request方法,定义类视图
# **locals: 可传递多个变量值
def dispatch_request(self):
return render_template('index4.html', **self.context)
def hello_world():
return url_for('index')
# 当使用类视图的时候,只能通过这种方式来注册路由
# 当如果想访问类视图的时候,如果指定了endpoint对应URL的名字的话,可以访问,如果没有指定的话,那就是通过as_view()指定的名字来访问
app.add_url_rule(rule='/', endpoint='index', view_func=Index.as_view('Index'))
app.add_url_rule(rule='/hello', view_func=hello_world)
4.2.2 基于方法的类视图
利用视图函数实现不同的请求执行不同的逻辑时比较复杂,需要在视图函数中进行判断,如果利用方法视图实现就比较简单。Flask提供了另外一种类视图flask.views.MethodView,对每个HTTP方法执行不同的函数(映射到对应方法的小写的同名方法上)。
index5.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<style>
.div1 {
height: 180px;
width: 380px;
border: 1px solid #8A8989;
margin: 0 auto;
}
.input {
display: block;
width: 350px;
height: 40px;
margin: 10px auto;
}
.button {
background: #2066C5;
color: white;
font-size: 18px;
font-weight: bold;
height: 50px;
border-radius: 4px;
}
</style>
<div class="div1">
{# action:接受的路由 #}
<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">
</form>
</div>
</body>
</html>
app.py
# @time:2020/11/8 10:09
# Author:Small-J
from flask import Flask, render_template, request, views
app = Flask(__name__)
@app.route('/')
def hello_world():
return render_template('index5.html')
# 当发送post请求时,可以使用MethodView方法来进行
#
class LoginView(views.MethodView):
# 当用户通过get方法进行访问的时候执行get方法
def get(self):
return render_template('index5.html')
# 当用户通过post方法进行访问的时候执行post方法
def post(self):
username = request.form.get('username')
password = request.form.get('pwd')
if username == 'admin' and password == 'admin':
return "用户名正确,可以登录"
else:
return "用户名或密码错误"
app.add_url_rule('/login', endpoint='login', view_func=LoginView.as_view('loginview'))
if __name__ == '__main__':
app.run(debug=True)
4.3 Flask装饰器
装饰器本质上是一个Python函数,它可以让其他函数在不需要的做任何代码变动的前提下增加额外的功能,装饰器的返回值也是一个函数对象。装饰器经常用于有切面需求的场景,比如插入日志、性能测试、事务处理、缓存和权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,可以抽离出大量与函数功能无关的雷同代码并继续重用。
4.3.1 装饰器的定义和基本使用
有这样一个应用场景:一个新闻站点,新闻列表页、新闻详情页均要求用户登录才能够浏览。它的实质就是一个装饰器。
def user_login(func):
def inner():
print('登录操作')
func()
return inner
- 注意: 装饰器其实就是一个函数,其参数是一个函数,返回值也是一个函数。返回值返回的不是函数的结果,return inner()表示返回的是函数的结果,return inner表示返回的是函数
4.3.2 对带参数的函数使用装饰器
有时给函数加装饰器的时候,这个函数是需要传递参数的,那么就涉及对带参数的函数使用装饰器的问题
# @time:2020/11/8 14:24
# Author:Small-J
from flask import Flask
from functools import wraps
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World'
def user_login(func):
@wraps(func)
def inner(*args, **kwargs):
print('登录操作!')
func(*args, **kwargs)
return inner
@user_login
def news():
print(news.__name__)
print('这是新闻详情页!')
news()
@user_login
def news_list(*args):
page = args[0]
print(news_list.__name__)
print('这是新闻列表页的第' + str(page) + '页')
news_list(5)
if __name__ == '__main__':
app.run(debug=True)
4.4 蓝图
随着业务代码的增加,将所有代码都放在单个程序文件中是非常不合适的,这不仅会让阅读代码变得困难,而且会给后期维护带来麻烦。Flask蓝图提供了模块化管理程序的功能,使程序结构倾斜、简单易懂。
一个程序执行文件中,如果功能代码过多,是不方便后期维护的。如何实现程序代码模块化,根据具体不同功能模块的实现,划分成不同的分类,降低各功能模块之间的耦合度flask.Blueprint就派上用途了。
- 蓝图的定义:在蓝图被注册到应用之后,所要执行的操作的集。当分配请求时,Flask会把蓝图和视图函数关联起来,并生成两个端点之前的URL。
- 注意:蓝图可以极大地简化大型应用,并为扩展提供集中的注册入口。Blueprint对象与Flask应用对象的工作方式类似,单不是有真正的应用。
- 注意: 视图函数的名称不能和蓝图对称的名称一样
app.py
# @time:2020/11/8 14:45
# Author:Small-J
from flask import Flask
import news, products
app = Flask(__name__)
@app.route('/')
def hello_world():
return '这是首页'
# register_blueprint: 注册蓝图
#
app.register_blueprint(news.new_list)
app.register_blueprint(products.products_list)
if __name__ == '__main__':
app.run(debug=True)
news.py
# @time:2020/11/8 14:46
# Author:Small-J
from flask import Blueprint
# Blueprint:第一个参数为蓝图的名字,第二个参数为引入的模块的文件,代表当前文件
new_list = Blueprint('news', __name__)
@new_list.route('/news')
def new():
return '这是新闻的模块'
products.py
# @time:2020/11/8 14:46
# Author:Small-J
from flask import Blueprint
products_list = Blueprint('products', __name__)
@products_list.route('/products')
def products():
return '这是产品模块'