Web后端学习笔记 Flask(4)视图函数
Flask中的视图函数以及视图类:
添加视图函数还可以通过下面的方式add_url_rule进行:
app.add_url_rule(rule, endpoint, view_func):
rule: 对应的url
endpoint:相当于给url取一个名字
view_func:视图函数
-------------------------------------------------------------------------------------------------------------------------------
endpoint用于反转视图函数得到url,如果给endpoint赋了值,那么在使用url_for()获取视图函数的url时,就不能再使用函数名作为参数,而是应该使用endpoint来获取视图函数的url
from flask import Flask, url_for
app = Flask(__name__)
app.config["TEMPLATE_AUTO_RELOAD"] = True
@app.route('/')
def hello_world():
print(url_for("theList")) # 这里url_for的参数值只能是theList, 不能是my_list
return 'Hello World!'
def my_list():
return "This is item list"
app.add_url_rule(rule="/list/", endpoint="theList", view_func=my_list)
if __name__ == '__main__':
app.run(debug=True)
标准类视图及其使用场景:
如何将类变成一个视图函数:类视图支持继承,写完类视图,需要通过add_url_rule进行添加
1. 标准类视图:
标准类视图继承自flask.views.View,并且在子类中必须实现dispatch_request方法,这个方法类似于视图函数,也要返回一个基于Response或者其子类的对象.
from flask import Flask, url_for, views
app = Flask(__name__)
app.config["TEMPLATE_AUTO_RELOAD"] = True
@app.route('/')
def hello_world():
return 'Hello World!'
class ListView(views.View):
def dispatch_request(self):
"""
类视图中必须实现这一个方法
:return:
"""
return "This is list view"
app.add_url_rule(rule="/list/", endpoint="list", view_func=ListView.as_view("list"))
# 这里的as_view会将一个对象转化为函数,赋值给视图函数, 参数name是给转换后的函数取的名字
if __name__ == '__main__':
app.run(debug=True)
如果制定了endpoint,那么在使用url_for时必须使用endpoint, 如果没有指定,那么就可以使用as_view中的名字。
类试图的应用:
a. 例如有url需要返回字典
from flask import Flask, views, jsonify
app = Flask(__name__)
app.config["TEMPLATE_AUTO_RELOAD"] = True
@app.route('/')
def hello_world():
return 'Hello World!'
class JsonView(views.View): # 定义基类
def get_data(self):
return NotImplementedError
def dispatch_request(self):
return jsonify(self.get_data())
class ListView(JsonView):
def get_data(self):
return {"username": "Tom", "password": "2345"}
app.add_url_rule(rule="/list/", endpoint="list", view_func=ListView.as_view("list"))
# 这里的as_view会将一个对象转化为函数,赋值给视图函数, 参数name是给转换后的函数取的名字
if __name__ == '__main__':
app.run(debug=True)
JsonView类是基类,定义get_data()方法,ListView类是JsonView类的基类,在基类中,重新实现了get_data()方法,在调用ListView中的dispatch_request()方法的时候,由于ListView中未重写dispatch_request()方法,所以回去调用父类中的dispatch_request()方法,在父类的dispatch_request()方法中会调用get_data()方法,而在子类ListView中实现了get_data方法,所以实际调用的是子类ListView中的方法,返回字典
b. 有几个url需要返回相同的变量:
例如,在登陆页面以及注册页面,需要展示相同的广告,则可以通过下面继承的方法实现:
from flask import Flask, views, render_template
app = Flask(__name__)
app.config["TEMPLATE_AUTO_RELOAD"] = True
@app.route('/')
def hello_world():
return 'Hello World!'
class AdsView(views.View): # 广告内容
def __init__(self):
super(AdsView, self).__init__() # 调用父类的init方法
self.ad_content = {
"product": "GMCC-1001"
}
def dispatch_request(self):
return NotImplementedError # 因为基类中的这个方法不会被调用
class LoginView(AdsView):
def dispatch_request(self):
return render_template("html/login.html", **self.ad_content)
class RegisterView(AdsView):
def dispatch_request(self):
return render_template("html/registry.html", **self.ad_content)
app.add_url_rule(rule="/login/", endpoint="login", view_func=LoginView.as_view("login"))
app.add_url_rule(rule="/registry/", endpoint="registry", view_func=RegisterView.as_view("registry"))
if __name__ == '__main__':
app.run(debug=True)
通过这种方法,可已将相同的广告内容展示在不同的页面。且修改起来更加的容易。
2. 基于请求方法的视图
Flask还提供了另一种类视图,flask.view.Methodview,对每个http执行不同的函数(映射到对应方法的小写的同名方法上),这对restful API尤其有用。
基于方法的类视图,是根据请求的“method”不同来执行不同的方法的,如果用户是发送的“get”请求,那么将会执行这个类的get方法,如果发送的是post请求,将会执行类的post方法。其他的“method”也类似,例如delete,put方法
from flask import Flask, views, render_template, request
app = Flask(__name__)
app.config["TEMPLATE_AUTO_RELOAD"] = True
@app.route('/')
def hello_world():
return 'Hello World!'
class LoginView(views.MethodView):
def get(self, error=None):
return render_template("html/login.html", error=error)
def post(self):
"""
获取post请求提交的参数
:return:
"""
username = request.form.get("username")
password = request.form.get("password")
if username == "knight" and password == "123456":
return self.get(error="success")
else:
return self.get(error="fail")
app.add_url_rule(rule="/login/", view_func=LoginView.as_view("login"))
if __name__ == '__main__':
app.run(debug=True)
前端代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登陆</title>
</head>
<body>
<form action="" method="post">
<table>
<tr>
<td>用户名</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>密码</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="点击登陆"></td>
</tr>
</table>
{% if error %}
{% if error == "success" %}
<p style="color: green">登陆成功</p>
{% else %}
<p style="color: red">登陆失败</p>
{% endif %}
{% endif %}
</form>
</body>
</html>
类视图中使用装饰器:
1. 如果使用的是函数视图,那么自己定义的装饰器必须放在“app.route()”装饰器的下面才能生效。否则这个装饰器不能起作用。
2. 类视图的装饰器需要重写类视图的一个属性decorates,这个类属性是一个列表或者元组,里面装的就是所有的装饰器。
应用场景:例如网站上的个人信息页面和设置页面,在跳转到这些页面的时候,如果用户没有登陆,则会自动跳转到登陆页面,
提醒用户登陆。只有在登录状态下才能访问这些页面,下面通过装饰器模拟这一功能。
from flask import Flask, views, render_template, request
from functools import wraps
app = Flask(__name__)
app.config["TEMPLATE_AUTO_RELOAD"] = True
def login_required(func): # 定义装饰器
@wraps(func) # 保留参数 函数func的一些属性,如__name__属性等
def wrapper(*args, **kwargs):
username = request.args.get("username")
if username and username == "Tom":
return func(*args, **kwargs)
else:
return "请先登陆"
return wrapper
@app.route('/')
def hello_world():
return 'Hello World!'
@app.route('/setting/')
@login_required # 自定义的装饰器应该放在url的下main
def setting():
return "User setting"
# 给类视图添加装饰器
class ProfileView(views.View):
decorators = [login_required]
def dispatch_request(self):
return "这是个人页面"
app.add_url_rule("/profile/", view_func=ProfileView.as_view("profile"))
if __name__ == '__main__':
app.run(debug=True)
在这些页面中,只有请求参数中有用户名参数的时候,才能够跳转到相应的页面,否则会提示登陆。
蓝图的基本使用:
蓝图用于将一个大型Flask项目分层解耦,进行模块化划分,可以将相同模块的视图函数放在同一个文件中,统一进行管理
例如,网站主要可以划分为新闻,电影,图书,个人中心四个模块,则可以按照如下的方式进行划分:
user.py文件:(初始化蓝图)
# -*- coding: utf-8 -*-
from flask import Blueprint
user_bp = Blueprint("user", __name__, url_prefix="/user") # 初始化蓝图
# 个人中心
@user_bp.route('/profile/')
def profile():
return "个人中心页面"
@user_bp.route("/settings/")
def settings():
return "设置页面"
news.py文件 (初始化蓝图), 同时在news.py中渲染html文件的方法不变:
# -*- coding: utf-8 -*-
from flask import Blueprint, render_template
news_bp = Blueprint("news", __name__, url_prefix="/news")
@news_bp.route("/news_list/")
def news_list():
return render_template("html/news_list.html")
@news_bp.route("/news_detail/")
def news_detail():
return "新闻详情"
在app.py文件中,注册上面创建的蓝图
from flask import Flask, views, render_template, request
from blueprints.user import user_bp
from blueprints.news import news_bp
app = Flask(__name__)
app.register_blueprint(user_bp)
app.register_blueprint(news_bp)
app.config["TEMPLATE_AUTO_RELOAD"] = True
@app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run(debug=True)
这样就将项目中的视图函数分模块进行管理。
url_prefix:url前缀,如果加了,则视图函数对应的url前面必须有前缀,且只能有一个斜杠
route中还可以指定template_folder参数,如果制定了这个参数template_folder=“xxx”,那么flask在渲染模板的时候,首先会去项目中的templates文件夹中寻找,如果没有找到,flask接下来会去blueprints/xxx文件夹中寻找模板文件。
蓝图中静态文件的寻找规则:
1. 在模板文件中,加载静态文件,如果使用url_for('static'),那么就会在app指定的静态文件夹目录下查找静态文件
2.如果在加载静态文件的时候,指定了蓝图的名字,比如“news.static”,那么Flask就会到这个蓝图指定的static_folder下查找静态文件。
url_for反转蓝图注意事项:
如果需要跨文件反转视图函数,则需要指定视图函数的蓝图名字,再加上视图函数名字,例如:
@app.route('/')
def hello_world():
print(url_for("news.news_list"))
return 'Hello World!'
例如,需要反转news_list视图函数,则需要在加上其蓝图的名字才可以,在模板中和在python文件中的用法一样,同样需要加蓝图的名字。
即使在同一个蓝图中反转视图函数,也需要指定蓝图的名字。
子域名实现详解:
1. 使用蓝图技术
2. 在创建蓝图对象的时候,需要传递一个"subdomain"参数,来指定这个子域名的前缀:
3. 需要在主app文件中,需要配置serve name参数:
【注】IP地址不能有子域名,localhost不能有子域名
例如,CMS子域名:
# -*- coding: utf-8 -*-
from flask import Blueprint
cms_bp = Blueprint("cms", __name__, subdomain="cms")
@cms_bp.route("/")
def index():
return "Cms index Page"
在app.py文件中设置:
from blueprints.cms import cms_bp
app.register_blueprint(cms_bp)
app.config["TEMPLATE_AUTO_RELOAD"] = True
app.config["SERVER NAME"] = "jd.com:5000"
# 修改host文件:
# 127.0.0.1 jd.com
# 127.0.0.1 cms.jd.com
此时设置完毕计算机的host文件后,在本地计算机访问jd.com,就会映射到127.0.0.1
---------------------------------------------------------------------------------------------------------------------------------------