SQLAlchemy 最佳实践:何时/如何配置 scoped_session?

she*_*ron 7 python sqlalchemy

我试图找出以“正确的方式”使用 SQLAlchemy 范围会话的正确方法,同时将定义会话的逻辑与配置分开并与使用会话分开。我多次被告知,一个好的方法是拥有一些全局 scoped_session 工厂,我可以在任何地方使用:

"""myapp/db.py
"""

from sqlalchemy.orm import sessionmaker, scoped_session

Session = scoped_session(sessionmaker())
Run Code Online (Sandbox Code Playgroud)

然后当我想使用它时:

"""myapp/service/dosomething.py
"""

from myapp.db import Session

def do_something(data): 
    """Do something with data
    """
    session = Session()
    bars = session.query(Bar).all()
    for bar in bars:
        bar.data = data
    session.commit()
Run Code Online (Sandbox Code Playgroud)

这似乎是正确的,但我的问题是在我看到的所有示例中,sessionmaker还会设置会话的一些参数,即最重要的是绑定引擎。这对我来说毫无意义,因为实际的数据库引擎将在导入myapp.db模块期间从全局范围内未知的配置中创建。

我所做的是在我的应用程序的“main”(或线程的 main 函数)中设置所有内容,然后假设会话是在其他地方配置的(例如在do_something()上面使用时):

"""myapp/main.py
"""

from sqlalchemy import create_engine
from myapp.db import Session
from myapp.service.dosomething import do_something

def main(): 
    config = load_config_from_file()
    engine = create_engine(**config['db'])
    Session.configure(bind=engine)

    do_something(['foo', 'bar'])
Run Code Online (Sandbox Code Playgroud)

这看起来是正确的方法吗?我还没有找到这种流程的任何好的例子,但我发现的大多数其他例子似乎过于简化或特定于框架。

she*_*ron 11

这是旧的,我从来没有接受下面的任何答案,但是在各种项目中传递@univerio 的评论和 3+ 年在 SQLAlchemy 中的持续使用,我现在选择的方法是继续完全按照我在 OP 中的建议:

  1. 创建一个myapp.db定义的模块Session = ScopedSession(sessionmaker())
  2. from myapp.db import Session在需要的地方导入
  3. 在我的应用程序main或相关的初始化代码中,执行以下操作:
def main(): 
     config = load_config_from_file()
     engine = create_engine(**config['db'])
     Session.configure(bind=engine)

     do_something(['foo', 'bar'])
Run Code Online (Sandbox Code Playgroud)

我已经在 Web 应用程序、命令行工具和长时间运行的后端进程中成功地使用了这种模式,并且迄今为止从未改变过它。Ot 很简单、可重复使用且效果很好,我会向在这里磕磕绊绊的任何人推荐它,因为他们问过自己和我 3 年前做过的一样的问题。

  • 为什么 Sqlalchemy 文档从一开始就没有明确说明这一点?一年中我曾多次遇到这个问题,但我仍然发现自己每次都在谷歌上搜索“最佳实践”......感谢分享兄弟! (2认同)
  • 也许有点偏离主题,但是;编写 Python 多年,同时测试其他一些语言和框架,例如 C#/.Net,Python 看起来真的很混乱,甚至连最基本的部分都不清楚,而其他框架则很“糟糕”,例如共享服务发现单例(数据库是常见的)。 (2认同)