【问题标题】:SQLAlchemy context manager value not being returned未返回 SQLAlchemy 上下文管理器值
【发布时间】:2023-09-12 06:20:02
【问题描述】:

我有以下代码,试图不必有重复的会话管理代码。问题是add_model 函数中的session.add 给了我None,因为需要调用flush 方法,以便session.add 给我带有它的id 的新对象。我不知道如何解决这个问题。

任何帮助将不胜感激。

from contextlib import contextmanager

@contextmanager
def session_scope():
    """Provide a transactional scope around a series of operations."""
    session = Session()
    try:
        yield session
        session.commit()
    except:
        session.rollback()
        raise
    finally:
        session.close()

def add_model(model):
    with session_scope as session:
        return db.session.add(model)

【问题讨论】:

  • 你想让add_model()返回什么?
  • 嗨@jwodder。我希望它返回添加的模型对象,并带有 id。当我调用这个函数 fwiw 时,我可以看到一个新行确实被持久化到我的数据库中。
  • session.add 给你None 因为它不返回任何东西(但在这种情况下,函数在Python 中隐式返回None)。它不会为您提供带有 id 的新对象。
  • 好的,谢谢。我对session.add 工作原理的心智模型并不完全正确。

标签: python sqlalchemy


【解决方案1】:

model 直到刷新或提交后才获得 ID,但由于您已经在函数内部提交,您可以在提交后返回对象,此时 SQLAlchemy 将使用主键:

def add_model(model):
    with session_scope as session:
        db.session.add(model)
    return model

【讨论】:

  • 啊,当然,我应该返回变异的模型对象。谢谢!
  • 如果model 是一个临时对象,在调用add_model 之后尝试访问model 的属性将导致DetachedInstanceError(除非禁用提交时过期)。
  • 是的,很好。我解决这个问题的方法是对对象进行深度复制,以便可以在其他地方访问它,我对此不太满意。我很好奇其他人是如何解决这个问题的。我不知道expire_on_commit,你猜这是最好的方法吗?
  • 你可以session.merge() 到一个新的会话,它会查询数据库并返回合并的实例,但是让一个函数使用这样的会话似乎有点奇怪。会话范围很可能应该在函数之外。
  • 是的,我在想我这样做的方式有点不理想。您能否指点我一些代码来演示会话范围如何在函数外部?