1、类视图
之前我们接触的视图都是函数,所以一般简称视图函数。其实视图也可以基于类来实现,类视图的好处是支持继承,但是类视图不能跟函数视图一样,写完类视图还需要通过app.add_url_rule(url_rule,view_func)来进行注册。
1.1 标准类视图
标准类视图是继承自flask.views.View,并且在子类中必须实现dispatch_request方法,这个方法类似于视图函数,也要返回一个基于Response或者其子类的对象。
# -*- encoding: utf-8 -*-
"""
@File : view_demo.py
@Time : 2020/4/16 10:33
@Author : chen
"""
from flask import Flask, render_template, url_for, request
from flask import views, jsonify
app = Flask(__name__)
@app.route("/") # 实际相当于调用了add_url_rule()方法实现的
def view_demo():
# 如果add_url_rule 给了endpoint,相当于给url起了一个名字,endpoint='name'
print(url_for("name")) # 这里的url_for("name")必须和endpoint='name'相同才能实现url反转的功能
return "首页"
def profile():
return "个人中心"
# 类视图 必须重写dispatch_request()方法
class ListView(views.View): # 这里必须写views.View
def dispatch_request(self): # 必须重写dispatch_request()方法,否则会主动抛出异常 源码中:raise NotImplementedError()
return "类视图"
def demo(self): # self.demo()进行调用该方法
return "测试"
# *********************************************************************************************************
# 返回json类型数据
class JsonView(views.View):
def dispatch_request(self):
response = self.get_response() # 调用子类中重写的方法,将返回数据传入jsonify函数中
return jsonify(response) # jsonify(response)将数据返回成json数据类型
def get_response(self):
raise NotImplementedError() # 主动抛出异常,代表在子类中必须要重写该方法
# 继承了类视图JsonView,就不用重写dispatch_request方法
class ListJsonView(JsonView):
def get_response(self):
return {'username': "xxxx"} # 子类中重写了get_response方法,返回数据至调用方法的父类中
# *********************************************************************************************************
# 添加url规则
app.add_url_rule('/profile/', endpoint='name', view_func=profile) # profile 注意不能加()
# 优先使用endpoint中命名的名称,然后再使用view_func命名的名称
app.add_url_rule('/list/', endpoint='list', view_func=ListView.as_view('list'))
app.add_url_rule('/listjson/', view_func=ListJsonView.as_view('listjson')) # 注意类名 子类名
if __name__ == '__main__':
app.run(debug=True)
add_url_rule注意事项:
第一个’/login/'是URL地址 ,第二个as_view(‘login’)是方法名,通过url地址来执行as_view(‘login’)的方法。
且优先使用endpoint中命名的名称,然后再使用view_func命名的名称
# 第一个'/login/'是url地址 第二个as_view('login')是方法名
app.add_url_rule('/login/', endpoint='list', view_func=LoginView.as_view('login'))
`
有时候不同项目开启会有缓存文件,此时开启其他的项目的时候就需要清空缓存文件,可以用快捷键:
Ctrl+F5或者Ctrl+Shift+R就可以清空缓存文件,再开启下一个项目。
`
标准类视图另一个功能:把公有部分的数据抽离出来,放在父类中,由子类继承之后传递
# -*- encoding: utf-8 -*-
"""
@File : methods_demo.py
@Time : 2020/4/16 20:32
@Author : chen
"""
from flask import Flask, render_template, url_for, request
from flask import views, jsonify
app = Flask(__name__)
# 返回公共的变量
class BaseView(views.View):
def __init__(self):
super().__init__() # 调用父类中的方法
self.context = { # 把公有的数据抽离出来,放在父类中,由子类继承之后传递
"name": "123" # context传数据到html模板里,用name传
}
class LoginView(BaseView):
def dispatch_request(self):
return render_template("login.html", **self.context)
# 返回公共的变量
class RegistView(BaseView):
def dispatch_request(self):
return render_template("regist.html", **self.context)
app.add_url_rule('/login/', view_func=LoginView.as_view('login'))
app.add_url_rule('/regist/', view_func=RegistView.as_view('regist')) # 注意类名 子类名
if __name__ == '__main__':
app.run(debug=True, port=8888)
1.2 基于调度方法的视图
Flask还为我们提供了另外一种类视图flask.views.MethodView,对每个HTTP方法执行不同的函数(映射到对应方法的小写的同名方法上)
from flask import Flask, render_template, url_for, request
from flask import views
from flask.views import MethodView
app = Flask(__name__)
class LoginView(views.MethodView):
# def rend_temp(self, *args, **kwargs): # 代码继续封装,将rend_template封装成单独的方法,方便调用
# return render_template('login.html',)
def get(self, error=None):
return render_template('login.html', error=error) # 进行封装
def post(self):
name = request.form.get("name")
password = request.form.get("password")
# 此时这一步需要登录数据库,验证账号密码正确性
if name == "123" and password == "123":
return "登陆成功"
else:
# return "账号或者密码错误,登陆失败!"
# return render_template("login.html", error="账号或者密码错误") # 如果账号或者密码错误则传递一个参数error到模板文件login.html中
return self.get("账号或者密码错误") # 把上面的返回语句封装到get函数中
# 添加url规则 必须要有 第一个'/login/'是url地址 第二个as_view('login')是方法名
app.add_url_rule('/login/', view_func=LoginView.as_view('login'))
if __name__ == '__main__':
app.run(debug=True, port=8888)
1.3 权限验证
用类视图的一个缺陷就是比较难用装饰器来装饰,比如有时候需要做权限验证的时候
# 登陆之后才能访问的装饰器
def login_required(func):
def wrapper(*args, **kwargs):
print(type(request.args)) # <class 'werkzeug.datastructures.ImmutableMultiDict'>
print(type(request)) # <class 'werkzeug.local.LocalProxy'>
username = request.args.get("username") # 从url中获取username参数
if username:
return func(*args, **kwargs)
# 返回fun参数和不确定参数,如果方法wrapper中没写(*args, **kwargs),这里也可以不用写,如果只是返回函数func,就相当于返回调用下面的seting()方法
else:
return "请先登录"
return wrapper # 返回内层函数wrapper,(*args, **kwargs)是不确定参数
@app.route("/settings/") # 装饰路由
@login_required # 装饰器 封装功能为了限制登录 顺序不能换
def seting():
return "个人中心设置"
**上面的双装饰器功能:
-
return func(*args, **kwargs)
返回fun函数和不确定参数,如果方法wrapper中没写(*args, **kwargs),这里也可以不用写,如果只是返回函数func,就相当于返回调用下面的seting()方法 -
@app.route("/settings/")
装饰路由 -
@login_required
装饰器,封装功能为了限制登录 ,顺序不能与装饰路由换
装饰器写完后,可以在类视图中定义一个属性叫做decorators,然后存储装饰器。以后每次调用这个类视图的时候,就会执行这个装饰器
class ProfileView(views.View):
# 在类中用装饰器,需要用decorators属性
decorators = [login_required] # 调用login_required方法当成装饰器
def dispatch_request(self):
return "个人中心"
app.add_url_rule('/proview/', view_func=ProfileView.as_view('proview'))
2、蓝图(重要部分)
之前我们写的url和视图函数都是处在同一个文件,如果项目比较大的话,这显然不是一个合理的结构,而蓝图可以优雅的帮我们实现这种需求。
主文件app.py的实现。
from flask import Flask
from blueprints.news import news_bp # 导入news.py模块 news_bp相当于下面的app
from blueprints.books import book_bp
app = Flask(__name__)
app.register_blueprint(news_bp) # news_bp进行注册
app.register_blueprint(book_bp) # book_bp进行注册
@app.route("/")
def index():
return "首页"
if __name__ == '__main__':
app.run(debug=True)
蓝图文件:news.py模块
from flask import Blueprint
news_bp = Blueprint('news', __name__)
@news_bp.route("/news/")
def news():
return "新闻页面显示"
蓝图文件:books.py模块
from flask import Blueprint
book_bp = Blueprint('books', __name__)
@book_bp.route("/books/")
def book_list():
return "图书页面显示"
@book_bp.route("/books/detail/<bid>")
def book_detail(bid):
return "图书ID %s " % bid
以后访问/news/,/books/detail/,都是执行的blueprint_demo_flask.py文件中的视图函数,这样就实现了项目的模块化。
url_prefix参数的使用
from flask import Blueprint
book_bp = Blueprint('books', __name__, url_prefix="/books/") # 当url_prefix修改过后
@book_bp.route("/") # 当上面的url_prefix修改过之后,这里的路由默认/books/开头
def book_list():
return "图书页面显示"
@book_bp.route("/detail/<bid>") # 当上面的url_prefix修改过之后,这里的路由默认/books/开头
def book_detail(bid):
return "图书ID %s " % bid
from flask import Blueprint
# url_prefix路由是从news/开始的
news_bp = Blueprint('news', __name__, url_prefix="/news/")
# /news/ url_prefix -----> /news/
@news_bp.route("/") # 当上面的url_prefix修改过之后,这里的路由就不用了写了,默认/news开头
def news():
return "新闻页面显示"
3、寻找静态资源文件
默认不设置任何静态文件路径,Jinja2会在项目的static文件夹中寻找静态文件。也可以设置其他的路径,在初始化蓝图的时候,Blueprint这个构造函数,有一个参数static_folder可以指定静态资源文件的路径。
# url_prefix路由是从news/开始的 template_folder="test_template_mudel" 相对路径,相对于当前文件news.py来说,创建的文件要和news.py在同一路径
news_bp = Blueprint('news', __name__, url_prefix="/news/", template_folder="test_template_mudel", static_folder='static')
# static_folder='static'路径是相当对于当前文件news.py的相对路径 即在'/blueprints/static/'路径下查找资源文件
# 'news'为蓝图名称
static_folder可以是相对路径(相对蓝图文件所在的目录),也可以是绝对路径。在配置完蓝图后,还有一个需要注意的地方是如何在模板中引用静态文件。在模板中引用蓝图,应该要使用蓝图名+.+static来引用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- 加载静态资源文件 默认加载的路径是 'blueprint_flask_demo/static/news.css' -->
<!-- <link rel="stylesheet" href="{{ url_for('static', filename='news.css') }}">-->
<!-- 加载静态资源文件 修改后的加载的路径是 'blueprint_flask_demo/blueprints/static/news.css' -->
<link rel="stylesheet" href="{{ url_for('news.static', filename='news.css') }}">
<!-- 使用蓝图名+.+static来引用, 即'news.static' -->
</head>
<body>
<h1>这是templates中的新闻首页</h1>
</body>
</html>
寻找静态模板文件html
跟静态资源文件一样,默认不设置任何模板文件的路径,将会在项目的templates中寻找模板文件。也可以设置其他的路径,在构造函数Blueprint中有一个template_folder参数可以设置模板的路径
from flask import Blueprint, render_template
# url_prefix路由是从news/开始的 template_folder="test_template_mudel" 相对路径,相对于当前文件news.py来说,创建的文件要和news.py在同一路径
news_bp = Blueprint('news', __name__, url_prefix="/news/", template_folder="test_template_mudel")
# /news/ url_prefix -----> /news/
@news_bp.route("/")
def news():
# return "新闻页面显示"
# 查找模板文件会先根据 template_folder="test_template_mudel"这个路径去寻找,找不到之后再查找默认路径templates/news.html
return render_template("news.html")
查找模板文件会先根据默认路径templates下寻找模板文件news.html,找不到模板html文件之后, 会根据template_folder="xxxxxx"这个路径进行查找模板文件。
4、url_for生成url:
用url_for生成蓝图的url,使用的格式是:蓝图名称+.+视图函数名称。比如要获取admin这个蓝图下的index视图函数的url。
from flask import Flask, url_for
from blueprints.news import news_bp # 导入news.py模块 news_bp相当于下面的app
from blueprints.books import book_bp
app = Flask(__name__)
app.register_blueprint(news_bp) # news_bp进行注册
app.register_blueprint(book_bp) # book_bp进行注册
@app.route("/")
def index():
print(url_for("news.news")) # 反转url news.是根据Blueprint('news')中的第一个参数来确定的 后一个是写方法名
print(url_for("books.book_detail", bid=5)) # 反转url book_detail是方法名称 bid=5是book_detail方法中需要传的参数
return "首页"
if __name__ == '__main__':
app.run(debug=True)
反转url : 第一个news.是根据Blueprint(‘news’)中的第一个参数来确定的,后一个news是写方法名。
反转url : book_detail是方法名称,bid=5是book_detail方法中需要传的参数。