3

我在思考如何处理烧瓶请求中过期的 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 执行选择查询,因为将集合session.commit()标记为已过期。foos如果我知道foos实际上无法更改,那么阻止len(foos)执行查询的正确方法是什么?同样,如果foos 发生了变化,使用单个查询而不是多次查询来刷新数据的正确方法是什么?

4

2 回答 2

5

如果您知道 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)

expire_on_commit=False 另外,我想知道您是否可以通过文档中配置的单个会话来处理它:

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

使用 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)
于 2013-02-15T16:50:42.250 回答
2

我有一个稍微不同的方法,我不推荐 99% 的情况。(但无论如何我都会分享)

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

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

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

  • 从缓存中提取数据的例程也可以将属性指定为延迟加载函数(这是在我从缓存中拉出数据时编写的)。这样,我可以将“用户帐户”对象的照片属性关联为将特定照片从缓存中拉出的函数。

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

对于 99% 的情况,我不建议这样做。但是在 1% 的情况下,当您尝试将缓存数据持久化时,我发现这是最好的解决方案。

于 2013-02-15T22:36:16.223 回答