我正在用python和sqlalchemy-0.7编写一个应用程序。它首先初始化sqlalchemy orm(使用声明),然后启动多线程Web服务器 - 我目前正在使用web.py进行快速原型设计,但将来可能会发生变化。我还将为预定作业添加其他“线程”等等,可能使用其他python线程。
从SA文档我明白我必须使用scoped_session()来获取线程本地会话,所以我的web.py应用应该看起来像这样:
import web
from myapp.model import Session # scoped_session(sessionmaker(bind=engine))
from myapp.model import This, That, AndSoOn
urls = blah...
app = web.application(urls, globals())
class index:
def GET(self):
s = Session()
# get stuff done
Session().remove()
return(stuff)
class foo:
def GET(self):
s = Session()
# get stuff done
Session().remove()
return(stuff)
这是处理会话的正确方法吗?
据我了解,我应该在每个方法都得到一个scoped_session,因为它会给我一个我事先无法获得的线程本地会话(比如在模块级别)。
另外,我应该在每个方法结束时调用.remove()或.commit()或something like them,否则会话仍将包含Persistent个对象,我将无法查询/访问其他线程中的相同对象?
如果那个模式是正确的,那么通过只编写一次,也许使用装饰器,它可能会变得更好?这样的装饰器可以获取会话,调用方法然后确保正确地处理会话。如何将会话传递给修饰函数?
答案 0 :(得分:20)
是的,这是正确的方法。
示例:
具有Flask扩展名的Flask-sqlalchemy微框架符合您的描述。它还在每个HTTP请求(“视图”函数)结束时自动执行.remove(),因此会话由当前线程释放。仅调用.commit()是不够的,你应该使用.remove()。
当不使用Flask视图时,我通常使用“with”语句:
@contextmanager
def get_db_session():
try:
yield session
finally:
session.remove()
with get_db_session() as session:
# do something with session
你可以创建一个类似的装饰。
Scoped会话创建一个DBMS连接池,因此这种方法比每个HTTP请求的打开/关闭会话更快。它也适用于greenlets(gevent或eventlet)。
答案 1 :(得分:2)
如果为每个请求创建新会话,并且每个请求都由单个线程处理,则无需创建范围会话。
您必须致电s.commit()
以使待处理的对象持久,即将更改保存到数据库中。
您可能还想通过调用s.close()
关闭会话。