是否有正确的方法可以防止SQLAlchemy在过期的对象上重新运行查询?

时间:2013-02-15 15:52:37

标签: python sqlalchemy flask flask-sqlalchemy

我无法解决如何在烧瓶请求中处理过期的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)

然后在page.html

{% for foo in foos %}
  {{ foo. name }}
{% endfor %}

然后,SQLAlchemy将对模板循环中的每个foo执行select查询,因为session.commit()foos集合标记为已过期。如果我知道foos无法实际更改,那么阻止执行len(foos)查询的正确方法是什么?同样,如果foos 已更改,使用单个查询而不是多个查询刷新数据的正确方法是什么?

2 个答案:

答案 0 :(得分:4)

如果您知道foos无法更新,为什么要发布db.session.commit()?如果有时候,那么只需要更新某些内容,就可以触发提交。

您只需在foos = Foo.query.all()行下方添加db.session.commit()即可。然后,这将只针对所有数据触发单个查询,而不是每行一个。

正如您所说,提交数据会将其设置为已过期,因此需要重新查询它们。也许您可以刷新会话而不是重新查询,有关in the SQLAlchemy documentation的更多信息,这似乎表明您可以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)

另外,我想知道您是否可以使用expire_on_commit=False from the documentation配置的单个会话来处理它:

  

“commit()的另一个行为是默认情况下它会在提交完成后使所有实例的状态到期。这样,当下次访问实例时,通过属性访问或它们存在于查询结果集,它们接收最新状态。要禁用此行为,请使用expire_on_commit = False配置sessionmaker“

使用Session.expunge

根据需要从会话中删除对象

@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)

答案 1 :(得分:2)

我有一个略有不同的方法,我不推荐99%的情况。 (但我会分享)

我对通过SqlAlchemy提取的数据进行了积极的缓存。为了假设实时SqlAlchemy对象和缓存数据之间的级别奇偶校验,我执行以下操作(引用https://gist.github.com/jvanasco/01af92e100769d52f7b8

  • 当数据进入缓存时,我将其转换为原始字典(即删除所有sqlalchemy信息。我只想要表格的信息)

  • 当我从缓存中提取数据时,我将其转换为“ObjectifiedDict”。这是一个只提供基于点的属性访问的字典 - 就像SqlAlchemy对象一样。

  • 从缓存中提取数据的例程也可以将属性指定为延迟加载的函数(这是在我从缓存中拉出来的时候写的)。通过这种方式,我可以将“Useraccount”对象的photo属性与将特定照片从缓存中拉出来的功能相关联。

使用这种方法,我的只读部分使用与应用程序的可写部分相同的模板 - 唯一的区别是,如果您查看视图,一个部分上的对象是dicts的版本,而其他部分是实际的SqlAlchemy。

我不推荐99%的情况。但是在你试图保持缓存数据的那些情况下,我发现这是最好的解决方案。