Flask如何在每个请求开始时启动新的SQLAlchemy事务?

时间:2015-12-23 17:30:12

标签: python flask sqlalchemy

我尝试使用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")]

1 个答案:

答案 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连接。