在多线程 sqlalchemy web 应用程序中推荐的scoped_session 使用模式是什么?

发布于 2024-10-29 08:50:48 字数 1237 浏览 2 评论 0 原文

我正在使用 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() 或 类似的东西在每个方法结束时,否则会话仍将包含持久 对象,我将无法在其他线程中查询/访问相同的对象?

如果该模式是正确的,则可以通过仅编写一次(也许使用装饰器)来使其更好?这样的装饰器可以获取会话,调用该方法,然后确保正确处理会话。如何将会话传递给装饰函数?

I'm writing an application with python and sqlalchemy-0.7. It starts by initializing the sqlalchemy orm (using declarative) and then it starts a multithreaded web server - I'm currently using web.py for rapid prototyping but that could change in the future. I will also add other "threads" for scheduled jobs and so on, probably using other python threads.

From SA documentation I understand I have to use scoped_session() to get a thread-local session, so my web.py app should end up looking something like:

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)

Is that the Right Way to handle the session?

As far as I understand, I should get a scoped_session at every method since it'll give me a thread local session that I could not obtain beforehand (like at the module level).

Also, I should call .remove() or .commit() or something like them at every method end, otherwise the session will still contain Persistent objects and I would not be able to query/access the same objects in other threads?

If that pattern is the correct one, it could probably be made better by writing it only once, maybe using a decorator? Such a decorator could get the session, invoke the method and then make sure to dispose the session properly. How would that pass the session to the decorated function?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

野却迷人 2024-11-05 08:50:48

是的,这是正确的方法。

示例:

带有 Flask 微框架/" rel="noreferrer">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

您可以创建一个类似的装饰器。

作用域会话创建一个 DBMS 连接池,因此这种方法比在每个 HTTP 请求时打开/关闭会话更快。它也可以很好地与 greenlet(gevent 或 eventlet)配合使用。

Yes, this is the right way.

Example:

The Flask microframework with Flask-sqlalchemy extension does what you described. It also does .remove() automatically at the end of each HTTP request ("view" functions), so the session is released by the current thread. Calling just .commit() is not sufficient, you should use .remove().

When not using Flask views, I usually use a "with" statement:

@contextmanager
def get_db_session():
    try:
        yield session
    finally:
        session.remove()

with get_db_session() as session:
    # do something with session

You can create a similar decorator.

Scoped session creates a DBMS connection pool, so this approach will be faster than opening/closing session at each HTTP request. It also works nice with greenlets (gevent or eventlet).

ペ泪落弦音 2024-11-05 08:50:48

如果为每个请求创建新会话并且每个请求都由单线程处理,则不需要创建作用域会话。

您必须调用s.commit()来使挂起对象持久,即将更改保存到数据库中。

您可能还想通过调用 s.close() 来关闭会话。

You don't need to create a scoped session if you create new session for each request and each request is handled by single thread.

You have to call s.commit() to make pending objects persistent, i.e. to save changes into database.

You may also want to close session by calling s.close().

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文