我尝试使用this method完全分离Flask和SQLAlchemy,但Flask似乎仍能检测到我的数据库并在每个请求开始时启动新事务。
db.py
文件创建一个新会话并定义一个简单的表模型:
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, String
engine = create_engine("mysql://web:kingtezdu@localhost/web_unique")
print("creating new session")
db_session = scoped_session(sessionmaker(bind=engine))
Base = declarative_base()
Base.query = db_session.query_property()
# define model of 'persons' table
class Person(Base):
__tablename__ = "persons"
name = Column(String(30), primary_key=True)
def __repr__(self):
return "Person(\"{0.name}\")".format(self)
# create table
Base.metadata.create_all(bind=engine)
和app.py
,一个使用SQLAlchemy会话和模型的简单Flask应用程序:
from flask import Flask, escape
app = Flask(__name__)
# importing new session
from db import db_session, Person
# registering for app teardown to remove session
@app.teardown_appcontext
def shutdown_session(exception=None):
db_session.remove()
@app.route("/query")
def query():
# query all persons in the database
all_persons = Person.query.all()
print all_persons
return "" # we use the console output
if __name__ == "__main__":
app.run(debug=True)
让我们运行:
$ python app.py
creating new session
* Running on http://127.0.0.1:5000/
* Restarting with reloader
creating new session
已经足够运行db.py
两次,但我们忽略了这一点,让我们访问网页/query
:
[]
127.0.0.1 - - [23/Dec/2015 18:20:14] "GET /query HTTP/1.1" 200 -
虽然我们只使用控制台输出,但我们可以看到我们的请求已得到解答。数据库中还没有Person
,让我们添加一个:
mysql> INSERT INTO persons (name) VALUES ("Marie");
Query OK, 1 row affected (0.11 sec)
Marie
现在是数据库的一部分,所以我们重新加载网页:
[Person("Marie")]
127.0.0.1 - - [23/Dec/2015 18:24:48] "GET /query HTTP/1.1" 200 -
正如您所见,会话已经了解Marie
。 Flask没有创建新会话。这意味着有一个新的交易开始了。将此与下面的计划python示例进行对比,看看差异。
我的问题是Flask如何在每个请求开始时启动新事务。 Flask不应该知道数据库,但似乎能够改变它的行为。
如果您不知道SQLAlchemy事务的内容是什么,则从Managing Transactions中提取此段落:
在回滚或提交后完成事务状态时, Session释放所有Transaction和Connection资源,以及 回到“开始”状态,这将再次调用新的连接 和Transaction对象作为发出SQL语句的新请求 接收。
因此,事务以提交结束,并将导致建立新连接,然后再次使会话读取数据库。实际上,这意味着当您想要查看对数据库所做的更改时,您必须提交:
首先在交互式python模式中:
>>> from db import db_session, Person
creating new session
>>> Person.query.all()
[]
切换到MySQL并插入新的Person
:
mysql> INSERT INTO persons (name) VALUES ("Paul");
Query OK, 1 row affected (0.03 sec)
最后尝试将Paul
加载到我们的会话中:
>>> Person.query.all()
[]
>>> db_session.commit()
>>> Person.query.all()
[Person("Paul")]
答案 0 :(得分:1)
我认为这里的问题是scoped_session
在某种程度上隐藏了正在使用的实际会话。当你的拆解处理程序
# registering for app teardown to remove session
@app.teardown_appcontext
def shutdown_session(exception=None):
db_session.remove()
在每个请求结束时运行,您调用db_session.remove()
来处理该特定请求中使用的会话以及任何事务上下文。有关详细信息,请参阅http://docs.sqlalchemy.org/en/latest/orm/contextual.html,尤其是
scoped_session.remove()方法首先调用Session上的Session.close() 当前的Session,具有释放任何效果 然后,会话首先拥有的连接/事务资源 丢弃会话本身。这里的“释放”意味着连接 返回到它们的连接池,任何事务状态都是 回滚,最终使用底层的rollback()方法 DBAPI连接。