烧瓶测试中无法正确删除sqlalchemy会话 [英] sqlalchemy session not getting removed properly in flask testing
问题描述
我正在使用烧瓶测试,其中说:
另一个陷阱是Flask-SQLAlchemy也删除了会话 每个请求结束时的实例(任何线程安全都应如此) 结合使用SQLAlchemy和scoped_session的应用程序).因此 每次您清除会话以及添加到其中的所有对象 调用client.get()或其他客户端方法.
Another gotcha is that Flask-SQLAlchemy also removes the session instance at the end of every request (as should any threadsafe application using SQLAlchemy with scoped_session). Therefore the session is cleared along with any objects added to it every time you call client.get() or another client method.
但是,我没有看到.该测试失败:
However, I'm not seeing that. This test fails:
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.testing import TestCase
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)
@app.route('/')
def index():
print 'before request:', `db.session`
u = db.session.query(User).first()
u.name = 'bob'
return ''
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
class SessionTest(TestCase):
def create_app(self):
return app
def test_remove(self):
db.drop_all()
db.create_all()
u = User()
u.name = 'joe'
db.session.add(u)
db.session.commit()
client = app.test_client()
client.get('/')
print 'after request:', `db.session`
print u.name
assert u not in db.session
(与$ nosetests test_file.py
一起运行以查看其运行情况.)
(Run with $ nosetests test_file.py
to see it in action.)
标准输出:
-------------------- >> begin captured stdout << ---------------------
before request: <sqlalchemy.orm.scoping.ScopedSession object at 0x10224c610>
after request: <sqlalchemy.orm.scoping.ScopedSession object at 0x10224c610>
bob
--------------------- >> end captured stdout << ----------------------
根据文档,获取请求后,用户u
不应在会话中,而是它!有人知道为什么会这样吗?
According to the docs, user u
should not be in the session after a get request, but it is! Does anybody know why this is happening?
此外,即使请求从未提交,u.name
还是bob
而不是joe
! (所以我确信这是同一堂课.)
Furthermore, u.name
is bob
and not joe
, even though the request never committed! (So I'm convinced it's the same session.)
作记录,
$ pip freeze | grep Flask
Flask==0.10.1
Flask-Bcrypt==0.5.2
Flask-DebugToolbar==0.8.0
Flask-Failsafe==0.1
Flask-SQLAlchemy==0.16
Flask-Script==0.6.2
Flask-Testing==0.4
Flask-Uploads==0.1.3
Flask-WTF==0.8
推荐答案
我很确定混淆来自SQLAlchemy中的会话受作用域的事实,这意味着每个请求处理程序都会创建和销毁自己的会话.
I'm pretty sure the confusion comes from the fact that sessions in SQLAlchemy are scoped, meaning that each request handler creates and destroys its own session.
这是必需的,因为Web服务器可以是多线程的,因此可以同时处理多个请求,每个请求都使用不同的数据库会话.
This is necessary because web servers can be multi-threaded, so multiple requests might be served at the same time, each working with a different database session.
由于这个原因,您在请求上下文之外使用的会话可能与处理'/'
路由的视图函数获得并最终销毁的会话不同.
For this reason, the session that you used outside of the context of a request is likely not the same session that the view function that handles the '/'
route gets and then destroys at the end.
更新:我仔细研究了一下,发现了这个问题.
UPDATE: I dug around a bit and figured this thing out.
Flask-SQLAlchemy在app.teardown_appcontext
上安装一个钩子,在这里它称为db.session.remove()
.
Flask-SQLAlchemy installs a hook on app.teardown_appcontext
, and here is where it calls db.session.remove()
.
测试环境不能完全复制真实请求的环境,因为它不会推送/弹出应用程序上下文.因此,永远不会在请求结束时删除会话.
The testing environment does not fully replicate the environment of a real request because it does not push/pop the application context. Because of that the session is never removed at the end of the request.
请注意,调用client.get()
时也不会调用在before_request
和after_request
中注册的函数.
As a side note, keep in mind that functions registered with before_request
and after_request
are also not called when you call client.get()
.
您可以通过对测试进行小的更改来强制应用程序上下文推送和弹出:
You can force an application context push and pop with a small change to your test:
def test_remove(self):
db.drop_all()
db.create_all()
u = User()
u.name = 'joe'
db.session.add(u)
db.session.commit()
with app.app_context():
client = app.test_client()
client.get('/')
print 'after request:', `db.session`
print u.name
assert u not in db.session
此更改使测试通过.
Flask-Testing的文档似乎是错误的,或者很可能已经过时了.也许事情在某些时候像它们描述的那样工作,但这对于当前的Flask和Flask-SQLAlchemy版本来说并不准确.
The documentation for Flask-Testing seems to be wrong or more likely outdated. Maybe things worked like they describe at some point, but that isn't accurate for current Flask and Flask-SQLAlchemy versions.
我希望这会有所帮助!
这篇关于烧瓶测试中无法正确删除sqlalchemy会话的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!