SQLAlchemy关系在flush后未更新

20

我有一些模型,它们之间定义了如下关系:

class Parent(Base):
    __tablename__ = 'parent'
    id = Column(Integer, primary_key=True, nullable=False)
    children = Relationship(Child, lazy='joined')

class Child(Base):
    __tablename__ = 'child'
    id = Column(Integer, primary_key=True, nullable=False)
    father_id = Column(Integer, ForeignKey('parent.id'), nullable=False)
如果我在会话中添加一个子项(使用session.add(Child(...))),那么在刷新会话后,我希望其父项的子项关系将更新以包括此子项。然而,我没有看到这一点。
parent = session.query(Parent).get(parent_id)
num_children = len(parent.children)
# num_children == 3, for example

session.add(Child(father_id=parent_id))
session.flush()

new_num_children = len(parent.children)
# num_children == 3, it should be 4!

非常感谢任何帮助!


我可以直接将新的子项添加到parent.children列表中,并刷新会话,但由于其他现有代码的原因,我想使用session.add来添加它。

我还可以在添加子项后执行commit,这确实会正确更新parent.children关系,但我不想在该点提交事务。

我尝试了向children关系添加一个backref,但似乎没有什么区别。


对于我来说,在类似的情况下,将子级和父级都添加到会话中有所帮助。 - Ryabchenko Alexander
1个回答

16
我刚遇到了这个问题。SQLAlchemy 内部使用了一些记忆化技术,以防止每次访问关系时都发出新的 SQL 查询。但是问题在于它似乎没有意识到直接更新外键可能会对关系产生影响。虽然 SQLAlchemy 可能可以通过修补来处理简单的连接,但对于复杂的连接会很困难,我认为这就是它行为方式的原因。
当你执行 session.flush() 时,你要将更改发送回数据库,但 SQLAlchemy 没有意识到需要查询数据库以更新关系。
如果在刷新后调用 session.expire_all(),那么你就强制 SQLAlchemy 在下一次访问模型实例和关系时重新加载它们 - 这解决了问题。
你还可以使用 session.expire(obj) 更有选择性地执行此操作,或使用 session.refresh(obj) 以更有选择性且立即重新查询数据库。
有关这些方法及其区别的更多信息,我找到了一篇有用的博客文章:https://www.michaelcho.me/article/sqlalchemy-commit-flush-expire-refresh-merge-whats-the-difference

官方文档:https://docs.sqlalchemy.org/en/13/orm/session_api.html


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接