这是我偶然发现的一个奇怪的错误,我不确定它为什么会发生,不管它是SQLAlchemy中的错误,还是SQLAlchemy中的错误,还是Python的任何我还不知道的特性.
我们使用Flask 0.11.1,Flask SQLAlchemy 2.1使用PostgreSQL作为DBMS.
示例使用以下代码更新数据库中的数据:
entry = Entry.query.get(1)
entry.name = 'New name'
db.session.commit()
当从Flask shell执行时,这完全可以正常工作,因此数据库配置正确.现在,我们用于更新条目的控制器稍微简化(没有验证和其他样板),如下所示:
def details(id):
entry = Entry.query.get(id)
if entry:
if request.method == 'POST':
form = request.form
entry.name = form['name']
db.session.commit()
flash('Updated successfully.')
return render_template('/entry/details.html', entry=entry)
else:
flash('Entry not found.')
return redirect(url_for('entry_list'))
# In the application the URLs are built dynamically, hence why this instead of @app.route
app.add_url_rule('/entry/details/<int:id>', 'entry_details', details, methods=['GET', 'POST'])
当我提交详细的表格时.html,我可以很好地看到更改,这意味着表单已经正确提交,是有效的,并且模型对象已经更新.然而,当我重新加载页面时,更改消失了,就好像它被DBMS回滚了一样.
我已经启用了app.config['SQLALCHEMY_ECHO'] = True
,我可以在手动提交之前看到"回滚".
如果我改变路由:
entry = Entry.query.get(id)
致:
entry = db.session.query(Entry).get(id)
正如在https://stackoverflow.com/a/21806294/4454028中所解释的,它确实按照预期工作,所以我猜Flask SQLAlchemy的Model.query
实现中存在某种错误.
然而,由于我更喜欢第一种 struct ,我对Alchemy做了一个快速修改,并重新定义了原来的query
@property
:
class _QueryProperty(object):
def __init__(self, sa):
self.sa = sa
def __get__(self, obj, type):
try:
mapper = orm.class_mapper(type)
if mapper:
return type.query_class(mapper, session=self.sa.session())
except UnmappedClassError:
return None
致:
class _QueryProperty(object):
def __init__(self, sa):
self.sa = sa
def __get__(self, obj, type):
return self.sa.session.query(type)
其中sa
是Alchemy对象(即控制器中的db
).
现在,这就是事情变得奇怪的地方:它仍然无法保存更改.代码完全相同,但DBMS仍在回滚我的更改.
我读到Alchemy可以在拆卸时执行提交,并try 添加以下内容:
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
突然间,一切正常.问题是:为什么?
难道不应该只在视图完成渲染后才进行拆卸吗?为什么即使代码相同,修改后的Entry.query
的行为也与db.session.query(Entry)
不同?