我有一个端点从我的数据库中删除一个对象。我用以下代码删除它:
SELECT A1.*, 'ActionType' as "OptionType"
FROM TableA A1;
我有一个API测试来测试创建对象的端点,然后使用端点将其删除。我试图在删除发生后断言它不在数据库中。
my_object = Object.query.get(id)
db.session.delete(my_object)
db.session.commit()
return json.dumps({'success': True}
我认为这与一些SQLAlchemy会话缓存有关。我试过my_object = MyObject()
db.session.add(my_object)
db.session.commit()
response = requests.delete('{}/my-object/{}'.format(
os.environ.get('MY_URL'),
my_object.id
))
self.assertTrue(response.json()['success']) // this passes
self.assertEqual(200, response.status_code) // this passes
result = db.session.query(MyObject).get(my_object.id)
print(result.id) // prints the id of the job even though it is deleted from the database
,db.session.flush()
无济于事。该对象实际上正在从数据库中删除。所以我希望查询结果为db.session.expire_all()
。
我在文档中看到了这一点,但还没有完全包裹我的脑袋。 when to commit and close a session
感谢您的帮助。
答案 0 :(得分:5)
因此,在您的测试代码中,您将对象添加到会话并提交它。它会保存到数据库中,并且是会话的标识映射。
然后你点击你的应用程序,它拥有自己的会话。它会删除对象并提交,现在它已从数据库中删除了。但...
您之前的会话对此一无所知,当您使用.get()时,它将返回其身份映射中的内容:带有ID的Python对象。除非你关闭会话或强制刷新数据库,否则它不会重新获取(我不记得OTOH如何做到这一点,但你可以,它在某个地方的文档中)。如果你使用了一个干净的第三个会话,它将有一个新的身份映射,并且不会持有对python对象的引用,所以你得到你期望的,即。没有结果。这是设计原因,因为Identity Map允许SQLAlchemy将一堆更改链接到一个最佳SQL查询中,该查询仅在您提交时触发。
所以是的,你看到身份地图中的取物仍然存在。 (你甚至可以在交互式解释器中打开它并且四处寻找)它是有道理的,因为你说有两个不同web请求的线程,另一个是在另一个请求删除对象时用一个对象做一些更长寿的东西。第一个线程不应该在使用该对象的Python代码上进行barf,因为这只会在代码中的任何地方触发随机异常。它应该只是认为它可以做它的事情,然后在提交上失败,触发回滚。
HTH
答案 1 :(得分:1)
经过大量阅读和测试后,我发现创建一个新会话是最简单的解决方案。即使记录是陈旧的,我也无法弄清楚如何从数据库中刷新记录。
这是我做过的一些阅读:
when to commit and close a session
understanding sqlalchemy session
以下是我通过使用Flask-SQLAlchemy
:
[my other imports...]
from flask.ext.sqlalchemy import SQLAlchemy
[my other code...]
def test_it_should_delete_my_object(self):
my_object = MyObject()
db.session.add(my_object)
db.session.commit()
response = requests.delete('{}/my-object/{}'.format(
os.environ.get('MY_URL'),
my_object.id
))
self.assertTrue(response.json()['success'])
self.assertEqual(200, response.status_code)
new_db = SQLAlchemy(app) // establishing a new connection
result = new_db.session.query(MyObject).get(my_object.id) // by using a new
self.assertIsNone(result)
谢谢大家的帮助。将对此进行更多研究。
答案 2 :(得分:0)
db.session.expunge_all()
"从此会话中删除所有对象实例..."
http://docs.sqlalchemy.org/en/latest/orm/session_api.html#sqlalchemy.orm.session.Session.expunge_all
每个请求db.session.remove()
后的简单触发器例如在带有SQLAlchemy scoped_session的Flask中:
@app.teardown_appcontext
def shutdown_session(exception=None):
db.session.remove()
" scoped_session.remove()方法一如既往地删除与该线程关联的当前Session(如果有)。但是,threading.local()对象的一个优点是,如果应用程序线程本身结束,该线程的“存储”也是垃圾收集的。因此,将线程局部作用域与生成并删除线程的应用程序一起使用实际上是“安全的”,而无需调用scoped_session.remove()。但是,事务的范围本身,即通过Session.commit()或Session.rollback()结束它们,通常仍然必须在适当的时间明确安排,除非应用程序实际关联线程的生命周期到交易的生命周期。"
http://docs.sqlalchemy.org/en/latest/orm/contextual.html#thread-local-scope http://docs.sqlalchemy.org/en/latest/orm/contextual.html#using-thread-local-scope-with-web-applications