标题
一:钩子函数:
from flask import Flask, make_response, Response, request
app = Flask(__name__)
@app.before_first_request
def inital():
"""
第一次请求之前调用,并且只会调用一次。
作用:项目的初始化操作
:return: 不要返回值,如果存在返回值会之间返回给前端,就进入不了视图函数
"""
print("请求之前,只调用一次")
@app.before_request
def process_request():
"""
每次请求之前调用
作用:封ip, 统一用户权限认证,用户信息的提取
:return:不需要返回值
"""
print("请求之前调用,每次请求都会调用")
@app.after_request
def process_response(response):
"""
# 每一次视图执行之后调用。
作用:拦截响应对象进行统一设置。
:param response:视图函数返回的响应。
:return: 一定要返回响应,不返回前端获取不到响应。
"""
print("我在请求之后被调用了,每次请求之后都会被调用")
return response
@app.teardown_request
def process_error(error):
"""
每次请求结束的时候调用
作用:判断处理异常,收尾工作
:param error:异常处理对象
:return:
"""
print(error)
print("我会接收异常信息")
@app.route('/login')
def login():
print("我是视图函数,我被调用了")
return ""
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000, debug=True)
运行结果:
二:蓝图:
- 1: 在模块的初始化文件,创建蓝图对象。
- 2:在视图文件中,利用蓝图对象绑定路由信息。
- 3:将蓝图对象注册到app中。
- 4:在模块的初始化文件中导入视图文件中的视图。
1:案例:
- 初始化文件做两件事:1:创建蓝图对象。 2:导入视图。
- 视图文件中一件事:利用蓝图对象绑定路由。
- 主初始化文件一件事:将蓝图对象注册到app中。
1: 在home的__init__文件中的代码:
from flask import Blueprint
"""
1: 蓝图中可以创建属于自己的模板和静态文件
2: 创建蓝图对象的参数: 蓝图名称, 导包路径
"""
# 1: 创建蓝图对象
home_bp = Blueprint("home", import_name=__name__)
from home.views import *
2: 在home的views文件中的代码:
from home import home_bp
"""
蓝图对象没有注册视图路由的能力
"""
@home_bp.route('/home')
def home_page():
return "home page"
3:main.py中的代码:
from flask import Flask, make_response, Response, request
from home import home_bp
app = Flask(__name__)
app.register_blueprint(home_bp)
@app.route('/login')
def login():
print("这是登录函数")
return "登录函数"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000, debug=True)
2: 循环导包问题:
- __init__初始化文件中导入视图函数,必须在创建完蓝图对象之后导包。
解析循环导包:
如下图,在初始化文件中,from . import views会导入views中的视图,但是views中又需要导入home_bp这个蓝图对象,所以又回到__init__初始化文件中导入蓝图对象。这样就造成循环导包。
解决方案:延迟导包。导入views的过程,放在创建蓝图对象之后。
3:蓝图的3个使用细节:
- 1:创建蓝图时, 可以通过 url_prefix参数 给蓝图定义的路由添加 统一的URL资源段前缀。
- 2:蓝图定义的路由, 其函数标记为 蓝图名.函数名。
- 3:蓝图也可以 设置请求钩子,但是只有访问该蓝图定义的路由时才会触发,实现局部监听。
案例一:修改__init__文件后,我们发现需要访问/home/home才能访问到视图,因为,蓝图设置一次,视图又设置一次。
from flask import Blueprint
"""
1: 蓝图中可以创建属于自己的模板和静态文件
2: 创建蓝图对象的参数: 蓝图名称, 导包路径
"""
# 1: 创建蓝图对象
home_bp = Blueprint("home", import_name=__name__, url_prefix="/home")
from home.views import *
案例二:蓝图中使用url_for根据函数名获取url路由:
注意:home.homepage:home是蓝图名,homepage是函数名。
from flask import url_for
from home import home_bp
"""
蓝图对象没有注册视图路由的能力
"""
@home_bp.route('/home')
def home_page():
url = url_for("home.home_page")
print(url)
return "home page"
案例三:蓝图中使用;钩子函数:
from flask import Blueprint
"""
蓝图中创建的钩子函数,只对当前蓝图起作用,对其他蓝图不起作用
"""
# 1: 创建蓝图对象
home_bp = Blueprint("home", import_name=__name__, url_prefix="/home")
@home_bp.before_request
def home_pre_prepare():
print("home_prepare")
from home.views import *
三:上下文:
- 1:上下文就是一个数据容器。
- 2:上下文分为请求上下文和应用上下文。
- 3:请求上下文分为request和session。
- 4:应用上下文分为current_app 和 g。
- 5:导包都是from flask import request, session, current_app, g。
- 6:本质都不是全局变量,底层通过线程进行了隔离。
- 7:生命周期:请求开始时创建,返回响应时销毁。
- 8:作用范围:默认只能视图函数内部使用,函数外部使用会报错。
1:请求上下文:
- 注意session的使用需要设置加密密钥。
from flask import Flask,request, session
app = Flask(__name__)
app.secret_key = 'renshanwen@@@'
@app.route('/login')
def login():
# 请求上下文的使用---request:
print(request.headers)
print(request.url)
print(request.remote_addr)
print(request.remote_user)
# 请求上下文的使用---session
session['user'] = 'renshanwen'
print(session.get('user'))
print()
return ""
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000, debug=True)
2:应用上下文:
<一>: current_app的使用:
作用:当模块中够不到app时,想调用app中的配置,使用current_app获取。
from flask import Flask,current_app
app = Flask(__name__)
@app.route('/login')
def login():
print(current_app.url_map)
return "hello"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000, debug=True)
<二>: g变量的使用:
- 1:能在钩子函数和视图函数进行参数传递。(重点)
- 2:能够在视图函数和自定义函数之间进行参数传递。
- 3:每一次请求之后会清空钩子函数内部的值。(注意)
案例一:验证钩子函数可以和视图函数之间进行传递。
from flask import Flask ,g
app = Flask(__name__)
@app.before_request
def process_request():
# 使用钩子函数保存
g.username = 'renshanwen'
@app.route('/login')
def login():
# 取除钩子函数的值
print(g.username)
return "hello"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000, debug=True)
案例二:验证视图函数和自定义函数之间进行参数传递。
方案:自己定义一个函数,然后在视图函数中使用钩子函数保存数据,然后调用自定义函数,再在自定义函数中使用钩子获取值。
视图函数
from flask import Flask, make_response, Response, request, g
from home import test_demo
app = Flask(__name__)
@app.route('/login')
def login():
g.name = 'renshanwen'
test_demo()
return "hello"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000, debug=True)
#自定义函数
def test_demo():
print(g.name)
运行结果:
四:综合认证:
1: 统一认证:
问题:我们所有的很多视图都需要获取登录身份,如果每个视图单独获取用户身份则会出现大量的代码冗余。
解决:在钩子函数中获取,在其他函数中直接使用。
from flask import Flask, session, g
app = Flask(__name__)
app.secret_key = 'test'
@app.before_request
def prepare():
# 使用g变量来获取用户名
g.name = session.get('username')
@app.route('/')
def index():
# 直接拿过来使用
if g.name:
return "欢迎回来, %s" % g.name
else:
return '首页'
if __name__ == '__main__':
app.run(debug=True)
2:反装饰:
五:应用配置:
- 1: 使用app.config配置字典添加配置信息[少量配置信息]
- 2: 使用app.config.from_object从配置类中架子啊配置信息
- 3: 使用app.config.from_envvar从环境变量中加载配置信息。
<案例一>:使用app.config配置。
app = Flask(__name__)
# 方案一:
app.config['DEBUG'] = True
app.config['SECRET_KEY'] = "python39"
app.config['TESTING'] = True
app.config['LJSON_AS_ASCII'] = False
@app.route('/login')
def login():
print(app.config['DEBUG'])
print(current_app.config['DEBUG'])
return "hello"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000, debug=True)
<案例二>:使用app.config.from_object配置。
1: 新建一个配置文件settings:
# 创建一个配置基类
class BaseConfig(object):
DEBUG = True
SECRET_KEY = "renshanwen"
class DevelopmentConfig(BaseConfig):
DEBUG = True
SQL_HOST = "127.0.0.1"
SQL_PORT = 3306
class ProductionConfig(BaseConfig):
DEBUG = False
SQL_HOST = "192.168.22.11"
SQL_PORT_MASTER = 3306
SQL_PORT_SLAVE = 8306
2: 在配置文件中加载所在类的配置:
from flask import Flask,current_app
app = Flask(__name__)
app.config.from_object(DevelopmentConfig)
<三>在环境变量中加载配置:
1:在一个文件中写配置信息。
2:将文件路径添加进环境变量。
3:使用环境变量加载配置。
案例:
1:创建一个隐私配置文件:
# secret_config.py
SECRET_KEY = 'heima123' # 隐私配置
2:配置环境变量:
export ENV_CONFIG="/xx/secret_config.py" # 设置隐私配置对应的环境变量
3:在main函数中使用:
# silent=True表示加载失败也不报错。
flask_app.config.from_envvar('ENV_CONFIG', silent=True)
六:工厂设计模式:
思路:我们可以将创建app设计为一个工厂类,我们传入不同的字段,给我们创建不同配置的app。
1: 在settings配置文件中暴露一个端口:核心代码是最后的字典。
# 创建一个配置基类
class BaseConfig(object):
DEBUG = True
SECRET_KEY = "renshanwen"
class DevelopmentConfig(BaseConfig):
DEBUG = True
SQL_HOST = "127.0.0.1"
SQL_PORT = 3306
class ProductionConfig(BaseConfig):
DEBUG = False
SQL_HOST = "192.168.22.11"
SQL_PORT_MASTER = 3306
SQL_PORT_SLAVE = 8306
config_dict = {
"dev": DevelopmentConfig,
"pro":ProductionConfig,
}
2:创建工厂类,并创建一个开发配置下的app。
from flask import Flask, make_response, Response, request
from settings import config_dict
def create_app(config_name):
# 1: 创建app对象
app = Flask(__name__)
# 2:从配置类中加载配置信息
config_class = config_dict[config_name]
app.config.from_object(config_class)
# 3: 从环境变量中加载配置信息
app.config.from_envvar("CONFIG", silent=True)
return app
app = create_app("dev")
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000, debug=True)