烧瓶测试中无法正确删除sqlalchemy会话 [英] sqlalchemy session not getting removed properly in flask testing

查看:62
本文介绍了烧瓶测试中无法正确删除sqlalchemy会话的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用烧瓶测试,其中说:

另一个陷阱是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_requestafter_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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆