flask源码解析之上下文为什么用栈
楔子
我在之前的文章《flask源码解析之上下文》中对flask上下文流程进行了详细的说明,但是在学习的过程中我一直在思考flask上下文中为什么要使用栈完成对请求上下文和应用上下文的入栈和出栈操作,而且栈所维护的无非不就是一个列表,我直接用一个列表去存储请求上下文和应用上下文不可以吗?或者说我用一个变量、字典其他任何可存储数据的数据类型不行吗?对于这个问题的解答,是我在理解离线脚本和 flask多app应用中才理解flask上下文中使用栈的精髓。对于为什么使用栈进行存储上下文,请耐心看我之前对离线脚本和flask多app应用的铺垫。
离线脚本
在项目的实际应用中,我们需要使用离线脚本完成不能作为后台功能的操作,例如:
1. 每天凌晨对还款用户进行短信提醒
2. 每天凌晨对数据库进行特定操作
3. 在项目开发完成交付他人进行测试之前,使用一个脚本完成对数据库的初始化操作
对于以上的所要实现的功能,他们都不能作为后台代码开发的一部分,只能使用一个离线的脚本完成上述操作。我们以需求2为例编写一个离线脚本:
from sansa import db
from sansa.models import Users
db.session.add(Users(name="大萨达所"))
db.session.commit()
在项目未启动情况下,运行此脚本抛出以下错误:
"D:\Program Files\Python36\python.exe" D:/Demo/s8/demo/sansa/数据插入的离线脚本.py
Traceback (most recent call last):
File "D:\Program Files\Python36\lib\site-packages\sqlalchemy\util\_collections.py", line 999, in __call__
return self.registry[key]
KeyError: <greenlet.greenlet object at 0x0000000003723930> During handling of the above exception, another exception occurred: Traceback (most recent call last):
File "D:/Demo/s8/demo/sansa/数据插入的离线脚本.py", line 3, in <module>
db.session.add(Users(name="大萨达所"))
File "D:\Program Files\Python36\lib\site-packages\sqlalchemy\orm\scoping.py", line 158, in do
return getattr(self.registry(), name)(*args, **kwargs)
File "D:\Program Files\Python36\lib\site-packages\sqlalchemy\util\_collections.py", line 1001, in __call__
return self.registry.setdefault(key, self.createfunc())
File "D:\Program Files\Python36\lib\site-packages\sqlalchemy\orm\session.py", line 2950, in __call__
return self.class_(**local_kw)
File "D:\Program Files\Python36\lib\site-packages\flask_sqlalchemy\__init__.py", line 141, in __init__
self.app = app = db.get_app()
File "D:\Program Files\Python36\lib\site-packages\flask_sqlalchemy\__init__.py", line 912, in get_app
'No application found. Either work inside a view function or push'
RuntimeError: No application found. Either work inside a view function or push an application context. See http://flask-sqlalchemy.pocoo.org/contexts/. Process finished with exit code 1
提示错误的原因是没有找到应用上下文对象的入栈操作,出现此问题的原因是:我们只是导入了db,但是项目原项目没有启动,更没有请求到来一说,我们执行的此离线脚本从头到尾就没有出现应用上下文,那么就更不会有应用上下文的入栈和出栈操作,所以db找到不到应用上下文,找不到应用上下文就无法导入连接数据库的配置信息。现在既然我们知道了出现问题的原因,解决此问题的方法就是我们手动执行应用上下文的创建、入栈和出栈操作,即要执行以下步骤:
1. app_ctx = self.app.app_context()
2. app_ctx.push()
3. app_ctx.pop(exc)
因此,编写的离线脚本为:
from sansa import db,create_app
from sansa.models import Users # 获取到生成app
app = create_app().app
# 创建app_ctx
app_ctx = app.app_context()
# app_ctx 入栈
app_ctx.push()
# 对数据库进行操作
db.session.add(Users(name="大萨达所"))
# 提交
db.session.commit()
app_ctx.pop()
由于 app_ctx 有 __enter__ 和 __exit__ 方法:
def __enter__(self):
self.push()
return self
def __exit__(self, exc_type, exc_value, tb):
self.pop(exc_value) if BROKEN_PYPY_CTXMGR_EXIT and exc_type is not None:
reraise(exc_type, exc_value, tb)
根据以上方法,离线脚本还可以这样写:
from sansa import db,create_app
from sansa.models import Users
app = create_app().app with app.app_context():
# 对数据库进行操作
db.session.add(Users(name="大萨达所"))
# 提交
db.session.commit()
# 释放当前的连接
db.session.remove()
这样,此脚本可实现在项目离线的情况下,按照指定的时间点执行该离线脚本完成对数据库的操作。
flask的多app应用
博主有在《flask源码解析之DispatcherMiddleware》一文中对flask多app应用的使用和源码流程进行过详细阐述,flask多app所实现的功能与蓝图相同,就是完成路由分发的功能。例如:
from werkzeug.wsgi import DispatcherMiddleware
from werkzeug.serving import run_simple
from flask import Flask, current_app,request app1 = Flask('app01') app2 = Flask('app02') @app1.route('/index/')
def index():
return "app01" @app2.route('/index2/')
def index2():
"""
存在的问题:
def __call__(self, environ, start_response):
# 获取当前请求的URL,script == '/index/'
script = environ.get('PATH_INFO', '')
path_info = ''
# '/' 在 '/index/' 中
while '/' in script:
# self.mounts == {'/sec': app2,}, '/' 不在 self.mounts 中
if script in self.mounts:
app = self.mounts[script]
break
script, last_item = script.rsplit('/', 1)
path_info = '/%s%s' % (last_item, path_info)
else:
app = self.mounts.get(script, self.app)
original_script_name = environ.get('SCRIPT_NAME', '')
environ['SCRIPT_NAME'] = original_script_name + script
environ['PATH_INFO'] = path_info # 这里得到的知识不包含前缀的url,那这样不是就丢失了吗
return app(environ, start_response)
:return:
"""
print(request.full_path) # 得到的是 /index2/? 且没有request.path_info
return "app2" # http://www.oldboyedu.com/index
# http://www.oldboyedu.com/sec/index2
dm = DispatcherMiddleware(app1, {
'/sec': app2,
}) if __name__ == "__main__":
run_simple('localhost', 5000, dm)
为什么用栈
在 执行离线脚本 + 多app应用下,执行如下脚本程序:
from sansa import db,create_app
from sansa.models import Users
app1 = create_app1().app
app2 = create_app2().app with app1.app_context():
# 对数据库进行操作
db.session.add(Users(name="大萨达所"))
# 提交
db.session.commit()
# 释放当前的连接
db.session.remove()
with app2.app_context():
db.session.add(Users(name="xxx"))
# 提交
db.session.commit()
# 释放当前的连接
db.session.remove()
对于上述离线脚本程序,入栈顺序为: app1.app_context() ---> app2.app_context() ,由于执行当前脚本程序只开了一个线程,因此在两个应用上下文入栈的时候会存放至同一个线程id所维护的栈中;出栈的时候按照当前线程id去匹配此线程id所对维护的栈,每一个应用上下文在出栈的时候都会获取栈顶元素,即出栈顺序为: app2.app_context() ---> app1.app_context() ,完成了后进先出的栈特点,因此需要栈的数据结构。
flask源码解析之上下文为什么用栈的更多相关文章
- flask源码解析之上下文
引入 对于flask而言,其请求过程与django有着截然不同的流程.在django中是将请求一步步封装最终传入视图函数的参数中,但是在flask中,视图函数中并没有请求参数,而是将请求通过上下文机制 ...
- Flask源码解析:Flask应用执行流程及原理
WSGI WSGI:全称是Web Server Gateway Interface,WSGI不是服务器,python模块,框架,API或者任何软件,只是一种规范,描述服务器端如何与web应用程序通信的 ...
- flask源码解析之session
内容回顾 cookie与session的区别: 1. session 是保存在服务端的键值对 2. cookie 只能保存4096个字节的数据,但是session不受限制 3. cookie保存在浏览 ...
- flask源码解析之DispatcherMiddleware
DispatcherMiddleware作用 实现多app的应用,完成路由分发的功能 如何使用 from werkzeug.wsgi import DispatcherMiddleware from ...
- flask 源码解析:上下文(一)
文章出处 https://www.cnblogs.com/jackchengcc/archive/2018/11/29/10025949.html 一:什么是上下文 每一段程序都有很多外部变量.只有 ...
- Flask源码解析:Flask上下文
一.上下文(Context) 什么是上下文: 每一段程序都有很多外部变量.只有像Add这种简单的函数才是没有外部变量的.一旦你的一段程序有了外部变量,这段程序就不完整,不能独立运行.你为了使他们运行, ...
- 用尽洪荒之力学习Flask源码
WSGIapp.run()werkzeug@app.route('/')ContextLocalLocalStackLocalProxyContext CreateStack pushStack po ...
- 给jdk写注释系列之jdk1.6容器(10)-Stack&Vector源码解析
前面我们已经接触过几种数据结构了,有数组.链表.Hash表.红黑树(二叉查询树),今天再来看另外一种数据结构:栈. 什么是栈呢,我就不找它具体的定义了,直接举个例子,栈就相当于一个很窄的木桶 ...
- Flask之 请求,应用 上下文源码解析
什么是上下文? 每一段程序都有很多外部变量.只有像Add这种简单的函数才是没有外部变量的.一旦你的一段程序有了外部变量,这段程序就不完整,不能独立运行.你为了使他们运行,就要给所有的外部变量一个一个写 ...
随机推荐
- "UX"将会是下一个Buzzword?
“用户体验非常重要”.“没有用户体验就没有产品”.“UX就是一切”.不知道从何时开始,用户体验(UX) 这个名词已经变得如此多见了,但是人们真正的认识.认清了什么是用户体验了吗?设计师们常挂在嘴边的用 ...
- li.active2有加强重要性的效果。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- 【Java】Eclipse在线安装SVN插件
安装环境 系统:win7系统 软件:eclipse(Mars.2 Release (4.5.2)) 安装步骤 1. 打开eclipse软件,点击菜单栏的help——>Install New So ...
- c++中类的静态成员对象
在c++中,可以声明一个静态的成员对象,但是此时仅仅声明,没有定义,也不会创建这个内部的静态成员对象.只有在类体外部定以后才能创建这个对象. #include<iostream> usin ...
- KBMMW 4.70.00 发布
We are happy to announce the release of kbmMW v. 4.70.00 Professional and Enterprise Edition. kbmMW ...
- HDU 1503 Advanced Fruits (LCS+DP+递归)
题意:给定两个字符串,让你求一个最短的字符串,并且这个字符串包含给定的两个. 析:看到这个题,我知道是DP,但是,不会啊...完全没有思路么,我就是个DP渣渣,一直不会做DP. 最后还是参考了一下题解 ...
- SecureCRT和乱码
示例: # ls /usr/local/r3c/bin/lib /bin/ls: /usr/local/r3c/bin/lib: ????????? 查看系统字符集设置: # locale LANG= ...
- Shell编程-07-Shell中的case语句
目录 基本语法 case示例 case语句总结 case语句相当于多分支的if/elif/else语句,而在使用case会让脚本看起来更简单工整.在case语句中,程序会将获取到的值与case ...
- EntityFramework 基本模式和Code-First的简单使用
1.Database-First Database First就是首先建立好数据库,或者存在现成的数据库也可以.然后在vs中添加ADO.Net实体数据模型,找到需要的数据库和表.它是以数据库设计为基 ...
- STL中的Vector相关用法
STL中的Vector相关用法 标准库vector类型使用需要的头文件:#include <vector>. vector 是一个类模板,不是一种数据类型,vector<int> ...