[英]How does Flask start a new SQLAlchemy transaction at the start of each request?
我嘗試使用這種方法將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 事務是什么,請閱讀從管理事務中提取的這段:
當回滾或提交后事務狀態完成時,會話釋放所有事務和連接資源,並返回到“開始”狀態,當接收到發出 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")]
我認為這里的問題是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 擁有的任何連接/事務資源,然后丟棄 Session 本身。 這里的“釋放”是指將連接返回到它們的連接池並回滾任何事務狀態,最終使用底層 DBAPI 連接的 rollback() 方法。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.