adh*_*ris 3 python sqlalchemy flask flask-sqlalchemy
我在思考如何处理 Flask 请求中过期的 sqlalchemy 对象时遇到了麻烦。假设我执行以下操作:
from models import Foo, Bar
@app.route("/page")
def page():
foos = Foo.query.all()
for foo in foos:
b = Bar(foo.data)
db.session.add(b)
db.session.commit()
return render_template('page.html', foos=foos)
Run Code Online (Sandbox Code Playgroud)
然后在page.html:
{% for foo in foos %}
{{ foo. name }}
{% endfor %}
Run Code Online (Sandbox Code Playgroud)
然后 SQLAlchemy 将对模板循环中的每个 foo 执行选择查询,因为session.commit()将foos集合标记为已过期。如果我知道无法foos实际更改,那么防止len(foos)执行查询的正确方法是什么?同样,如果foos 已更改,使用单个查询而不是多个查询刷新数据的正确方法是什么?
如果您知道 foos 无法更新,为什么还要发布db.session.commit()?如果是有时,则在其中放入一些逻辑,仅在某些内容已更新时才触发提交。
您可以foos = Foo.query.all()在该db.session.commit()行下方添加一个。然后只会对所有数据触发一个查询,而不是每行一个。
正如您所说,提交数据会将其设置为已过期,因此需要重新查询它们。也许您可以刷新会话而不是重新查询,SQLAlchemy 文档中的更多信息似乎表明您可以这样做session.refresh(object).
您可以使用第二个会话,您将使用它来查询Foo,然后另一个会话来处理Bars. 当您提交时,这将使 foos 保持不变,因此您不必再次点击它。
这是一个粗略的例子:
from flask.ext.sqlalchemy import Session
@app.route('/example/')
def home():
session_two = Session(bind=db.engine.connect())
foos = session_two.query(Foo).all()
for foo in foos:
db.session.add(Bar(foo))
db.session.commit()
return render_template_string('''
{% for foo in foos %}
{{ foo.name }}
{% endfor %}
''', foos=foos)
Run Code Online (Sandbox Code Playgroud)
另外,我想知道您是否可以通过单个会话来处理它,这是expire_on_commit=False 从文档中配置的:
“commit() 的另一个行为是,默认情况下,它会在提交完成后使所有实例的状态过期。这样在下次访问实例时,通过属性访问或通过它们出现在查询结果集中,他们会收到最新的状态。要禁用此行为,请使用 expire_on_commit=False 配置 sessionmaker”
根据需要从会话中删除对象
@app.route('/')
def home():
foos = Foo.query.all()
for foo in foos:
db.session.add(Bar(foo))
db.session.expunge(foo)
db.session.commit()
return render_template_string('''
{% for foo in foos %}
{{ foo.name }}
{% endfor %}
''', foos=foos)
Run Code Online (Sandbox Code Playgroud)