正确的方法来防止SQLAlchemy在过期的对象上重新运行查询? [英] Proper way to prevent SQLAlchemy from re-running queries on expired objects?

查看:219
本文介绍了正确的方法来防止SQLAlchemy在过期的对象上重新运行查询?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果在瓶子请求中处理过期的sqlalchemy对象,我无法包装我的头。假设我做了如下的事情:

  from models import Foo,Bar 

@app。 $($ / $)
def page():
foos = Foo.query.all()
$ b $ f for foo:
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。然后,SQLAlchemy将为每个对象执行一个select查询因为 session.commit()标记为 foos 集合已过期。如果我知道没有办法让 foos 实际发生变化,那么防止 len(foos)的正确方法是什么c>正在执行的查询?同样的,如果 foos has 改变了,用单个查询刷新数据的正确方法是什么? $ b

解决方案

如果你知道没有办法更新foos,为什么要发出 db.session.commit() 所有?如果有时,那么只要有更新的东西,就会触发提交。



您可以添加一个 foos = Foo。 query.all() db.session.commit()行之下。那么就只是为所有的数据启动一个查询,而不是每行一个。

正如你所说,提交数据将设置为过期,所以他们会需要重新查询。也许你可以刷新会话,而不是重新查询,有关在SQLAlchemy文档中,似乎表明你可以做 session.refresh(object)



Update:Using Two Sessions



您可以使用第二个会话来查询 Foo ,然后在另一个会话中处理 Bars 。当你提交的时候,这将不会影响你的操作,所以你不必再次打开它。



这里有一个简单的例子:


$ from flask.ext.sqlalchemy import Session
$ b $ app_route('/ example /')
def home() :
session_two = Session(bind = db.engine.connect())
foos = session_two.query(Foo).all()
$ b $ f for 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()的另一个行为是默认的在提交完成之后,所有实例的状态都会到期。这样,当下次访问实例时,无论是通过属性访问还是通过查询结果集中存在的实例,它们都会收到最近的状态。要禁用此行为,请将sessionmaker配置为expire_on_commit = False



使用Session.expunge



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

  @ app.route('/')
def home():
foos = Foo.query.all()$ b $ f for 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)


I'm having trouble wrapping my head around how to deal with expired sqlalchemy objects in a flask request. Let's say I do something like the following:

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)

and then in page.html:

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

SQLAlchemy will then do a select query for each foo in the template loop, because the session.commit() marked the foos collection as expired. If I know that there is no way for foos to actually have changed, what is the right way to prevent len(foos) queries from being executed? Similarly, if foos has changed, what is the right way to refresh the data with a single query rather than many?

解决方案

If you know there is no way for foos to have been updated, why ever issue the db.session.commit() at all? If it's sometimes, then put some logic in that triggers the commit only if something has been updated.

You could just add a foos = Foo.query.all() underneath the db.session.commit() line. That would then just fire a single query for all the data, not one per row.

As you say, committing the data would set it as expired, so they'll need to be re-queried. Perhaps you could refresh the session rather then re-querying, more information on that in the SQLAlchemy documentation which seems to indicate you could do session.refresh(object).

Update: Using Two Sessions

You could use a second session, you'll use it to query the Foo, then the other session to handle the Bars. That will leave foos untouched when you commit, so you won't have to hit it up again.

Here's a rough example:

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)

Also, I wonder if you could handle it with a single session, that's been configured with expire_on_commit=False from the documentation:

"Another behavior of commit() is that by default it expires the state of all instances present after the commit is complete. This is so that when the instances are next accessed, either through attribute access or by them being present in a Query result set, they receive the most recent state. To disable this behavior, configure sessionmaker with expire_on_commit=False"

Using Session.expunge

Remove the object from the session as required

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

这篇关于正确的方法来防止SQLAlchemy在过期的对象上重新运行查询?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆