Flsak高级应用
1.请求钩子
@app.before_request
def process_request():
""""
# 在请求发送过来的时候调用,视图函数执行之前调用
# 作用:拦截请求,封ip,用户权限认证(cookie seession jwt)
black_list = []
if request.remote_addr in black_list:
# 注意:在请求之前就已经返回响应对象,那么就不会再执行视图函数
return "IP不允许访问"
"""
name = "xiaoming"
g.name = name
print("before_request 被调用了")
@app.before_first_request
def inital():
# 作用:实现项目的初始化工作:连接数据库...
print("before_first_request 被调用了")
@app.route('/')
def hello_world():
print(g.name)
g.user_id = 66
get_userid()
# 执行视图函数
return 'Hello World!'
def get_userid():
print(g.user_id)
# 注意:接受一个响应对象
@app.after_request
def process_response(resp):
# 视图函数执行之后调用
# 作用:拦截响应对象,统一设置处理
print("after_request 被调用了")
return resp
@app.teardown_request
def process_error(error):
# 作用:拦截异常,做异常处理,收尾工作
# error 有值-代表有异常 为空-代表没有异常发生
print("teardown_request 被调用了")
"""
装饰器两种执行方式:
方法1:语法糖
@app.after_request
def func():
pass
方法2:装饰器理解成函数
@app.after_request(process_response)
"""
def view_func():
pass
app.before_request(view_func)
2.蓝图
app = Flask(__name__)
# 1.创建蓝图对象-管理各自子模块 [麻雀虽小五脏俱全 类似app对象]
# 参数1:蓝图名称
# 参数2:导包路径
# 参数3:当前模块url访问前缀
home_bp = Blueprint('home', __name__, url_prefix='/home')
# 蓝图中也能实现钩子函数
# 局部请求钩子-作用范围是在home模块中
@home_bp.before_request
def process_request():
# request ==> 容器 ==> 请求相关是所有信息
print("拦截请求对象")
# 反解析函数-获取函数对应的url路由地址
# url_for("函数名称")
# url_for("蓝图名称.函数名称")
# url_for("user.user_func")
# 延迟导包-解决循环导包问题
from . import views
# 2.使用蓝图对象绑定路由和视图函数
from home import home_bp
# 2.使用蓝图对象绑定路由和视图函数
@home_bp.route('/index')
def home_func():
return "home page"
# 3.在app中`注册蓝图`对象[各自子模块需要被总的Flask应用管理起来]
app.register_blueprint(home_bp)
app.register_blueprint(user_bp)
3.上下文
"""
上下文:相当于一个数据容器
请求上下文:request, session
应用上下文:current_app, g
生命周期:[请求开始 返回响应]
使用范围:[默认情况只能在视图函数内使用]
Working outside of request context 超出了请求上下文的使用范围
Working outside of application context. 超出了应用上下文的使用范围
# 手动开启应用上下文
with app.app_context():
"""
# print(request) 错误 超出了请求上下文的使用范围
app = Flask(__name__)
app.secret_key = "xapsoop(**"
# with app.app_context():
# print(current_app)
# with app.request_context("模拟前端请求"):
# print(request)
@app.route('/index')
def index():
# 请求上下文
print(request.url)
print(request.method)
session["name"] = 'zs'
print(session.get('name'))
# 应用上下文
# current_app 当前运行的app对象
# 作用:当其他模块无法导入app的时候,使用current_app代替app
print(current_app.url_map)
# g 预留给程序的容器, 【重点】
# 1.方便在`钩子函数`和`视图函数`之间进行参数传递
# 2.方便在`视图函数`和自定义的`函数`之间进行参数传递
# g 对象生命周期 本次请求[请求开始 返回响应] 特点下一次请求在访问g对象中的数据将会被清空
g.user_name = 'curry'
# 别的模块中的函数也能使用g对象提取保存的内容
get_username()
return 'Hello World!'
@app.route('/profile')
def profile():
# 报错 user_name属性不存在
# 原因:每一次请求都会清空g对象,之前保存的数据就不存在了
# print(g.user_name)
pass
问题1: 上下文变量是否为全局变量?
不是全局变量, web服务器会通过多线程并发调用web应用, 而全局变量会被所有线程共享, 无法记录并发的多个请求数据上下文机制实现了线程隔离(LocalStack类型, 本质是字典, key是线程id, 值是上下文变量), 每个线程存取自己的数据, 相互不影响 请求1 -> 线程1 -> request = 请求1请求2 -> 线程2 -> request = 请求2
问题2: 上下文为什么设置使用范围?
主要目的为节省内存请求开始时, 创建上下文(记录上下文变量);请求结束时, 销毁上下文(将上下文变量删除, 数据占用的空间被释放)拓展阅读: 上下文原理
4.综合认证(统一处理,访问限制)
"""
需求:在请求钩子函数中统一提取用户信息,保存到g对象中,方便在其他视图函数中提取用户信息 【优化代码冗余】
需求:点赞、评论、个人中心视图函数要求必须登录才能访问,首页登录显示登录的用户,否则去登录
判断是否有登录 代码会冗余 -- 装饰器
"""
def login_required(view_func):
# 防止装饰器修改被装饰函数的名称和文档信息
@wraps(view_func)
def wrapper(*args, **kwargs):
# 1.额外装饰的代码实现
# 判断是否登录
if g.user_name and g.user_id:
# 已经登录
# 2.执行原函数代码实现
return view_func(*args, **kwargs)
else:
# 未登录
# 401 权限认证失败
abort(401)
return wrapper
# 注意:先装饰路由,再装饰登录装饰器
@app.route('/like')
@login_required
def like():
# 判断是否有登录
print("点赞成功")
return "点赞成功"
@app.before_request
def get_userinfo():
# 统一提取用户信息,保存到g对象中
g.user_name = session.get("user_name")
g.user_id = session.get("user_id")
@app.route('/login')
def login():
# 用户信息状态保持
session["user_name"] = "james"
session["user_id"] = 23
return 'login success'
@app.route('/')
def index():
if g.user_name and g.user_id:
return "欢迎: {}".format(g.user_name)
else:
return "<a href='/login'>去登录</a>"
@app.route('/comment')
@login_required
def comment():
# 判断是否有登录
print("comment 成功")
return "comment 成功"
@app.route('/profile')
@login_required
def profile():
# 判断是否有登录
print("修改个人资料")
return "profile page"
5.应用配置
# 方案1:
# 需求:少量配置信息可以通过配置字典添加
# config 本质是字典
# app.config["DEBUG"] = True
# app.config["SECRET_KEY"] = "python38"
# app.config["JSON_AS_ASCII"] = False
# 方案2:[推荐]
# 从配置类中加载配置信息
app.config.from_object(config_dict['pro'])
# 从环境变量中加载配置信息
# app.config.from_envvar()
# 从配置py文件中读取配置信息
# app.config.from_pyfile()
@app.route('/')
def hello_world():
# 获取配置信息
print(app.config.get("DEBUG"))
print(current_app.config.get("SECRET_KEY"))
print(current_app.config.get("JSON_AS_ASCII"))
print(current_app.config.get("SQL_URL"))
return 'Hello World!'
工厂模式
class BaseConfig(object):
"""配置类父类"""
# session混淆加密字符串
SECRET_KEY = "python666"
# 不允许中文转换成ASCII编码
JSON_AS_ASCII = False
class DevelopmentConfig(BaseConfig):
"""开发模式配置信息"""
# 开启调试模式
DEBUG = True
# SQL连接信息
SQL_URL = "127.0.0.1"
SQL_PORT = 3306
class ProductionConfig(BaseConfig):
"""生成模式配置信息"""
# 减少io开销
DEBUG = False
# SQL数据库连接信息
SQL_URL = "192.168.22.33"
SQL_PORT_MASTER = 3306
SQL_PORT_SLAVE = 8306
class TestingConfig(BaseConfig):
"""测试环境配置信息"""
DEBUG = True
TESTING = True
# 给别的模块调用提供一个接口
config_dict = {
"dev": DevelopmentConfig,
"pro": ProductionConfig,
"test": TestingConfig
}
# 23设计模式 https://yq.aliyun.com/topic/122
# 单例设计模式[补充] 工厂设计模式 装饰器设计模式 中间人设计思想(生产者消费者) MVC MVT MVVM....
# 传入了不同的配置参数,得到不同的配置类,给app添加不同的配置信息,进而得到不同环境下的app对象
def create_app(config_name):
"""工厂方法"""
app = Flask(__name__)
# 方案2:[推荐]
# 1.从配置类中加载配置信息
app.config.from_object(config_dict[config_name])
# 2.从环境变量中加载隐私配置,如果存在相同的配置信息,后加载的会覆盖之前加载的配置信息
# /Users/chenqian/Desktop/深圳38期Flask项目/Flask基础day02/03-代码/secret_config.py
# 添加环境变量配置信息需要填写绝对路径
# slient=True 即使未配置环境变量也不会报错
# export CONFIG 路径
app.config.from_envvar("CONFIG", slient=True)
return app
# 开发环境的app对象
app = create_app("dev")
# 生成环境的app对象
# app = create_app("pro")