sqlalchemy会话未在烧瓶测试中正确删除

mun*_*nch 5 python flask flask-sqlalchemy

我正在使用Flask-Testing,它说:

另一个问题是Flask-SQLAlchemy还会在每个请求结束时删除会话实例(正如使用带有scoped_session的SQLAlchemy的任何线程安全应用程序一样).因此,每次调用client.get()或其他客户端方法时,会话都会与添加到其中的任何对象一起清除.

但是,我没有看到.此测试失败:

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
Run Code Online (Sandbox Code Playgroud)

(运行$ nosetests test_file.py以查看它的运行情况.)

标准输出:

-------------------- >> 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 << ----------------------
Run Code Online (Sandbox Code Playgroud)

根据文档,用户u不应该在获取请求后进入会话,但它是!有人知道为什么会这样吗?

此外,u.namebob不是joe,即使请求从未提交过!(所以我确信它是同一个会议.)

作为记录,

$ 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
Run Code Online (Sandbox Code Playgroud)

Mig*_*uel 10

我很确定混淆来自于SQLAlchemy中的会话是作用域的,这意味着每个请求处理程序都会创建并销毁自己的会话.

这是必要的,因为Web服务器可以是多线程的,因此可以同时提供多个请求,每个请求使用不同的数据库会话.

因此,您在请求上下文之外使用的会话可能与处理'/'路由的视图函数获取的会话相同,然后在最后销毁.

更新:我挖了一下,想出了这个东西.

Flask-SQLAlchemy安装了一个钩子app.teardown_appcontext,这里是它调用的地方db.session.remove().

测试环境不会完全复制实际请求的环境,因为它不会推送/弹出应用程序上下文.因此,在请求结束时永远不会删除会话.

作为一个方面说明,保持在功能登记的头脑before_requestafter_request当你打电话也没有叫client.get().

您可以通过对测试进行少量更改来强制应用程序上下文推送和弹出:

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
Run Code Online (Sandbox Code Playgroud)

通过此更改,测试通过.

Flask-Testing的文档似乎是错误的或更可能过时.也许事情就像他们在某些时候描述的那样,但这对于当前的Flask和Flask-SQLAlchemy版本来说并不准确.

我希望这有帮助!