使用双向association_proxy时自动传播删除

mic*_*eyh 9 python sqlalchemy

我正在使用双向association_proxy关联属性Group.membersUser.groups.我遇到了从中删除成员的问题Group.members.特别是,Group.members.remove将成功删除一个条目Group.members,但将留下一个None代替相应的条目User.groups.

更具体地说,以下(minimal-ish)代表性代码段失败了它的最后一个断言:

import sqlalchemy as sa

from sqlalchemy.orm import Session
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.ext.declarative import declarative_base


Base = declarative_base()


class Group(Base):
    __tablename__ = 'group'
    id = sa.Column(sa.Integer, autoincrement=True, primary_key=True)
    name = sa.Column(sa.UnicodeText())
    members = association_proxy('group_memberships', 'user',
            creator=lambda user: GroupMembership(user=user))


class User(Base):
    __tablename__ = 'user'
    id = sa.Column(sa.Integer, autoincrement=True, primary_key=True)
    username = sa.Column(sa.UnicodeText())
    groups = association_proxy('group_memberships', 'group',
            creator=lambda group: GroupMembership(group=group))


class GroupMembership(Base):
    __tablename__ = 'user_group'
    user_id = sa.Column(sa.Integer, sa.ForeignKey('user.id'), primary_key=True)
    group_id = sa.Column(sa.Integer, sa.ForeignKey('group.id'), primary_key=True)

    user = sa.orm.relationship(
            'User',
            backref=sa.orm.backref('group_memberships', cascade="all, delete-orphan"))
    group = sa.orm.relationship(
            'Group',
            backref=sa.orm.backref('group_memberships', cascade="all, delete-orphan"),
            order_by='Group.name')


if __name__ == '__main__':
    engine = sa.create_engine('sqlite://')
    Base.metadata.create_all(engine)
    session = Session(engine)

    group = Group(name='group name')
    user = User(username='user name')
    group.members.append(user)
    session.add(group)
    session.add(user)
    session.flush()
    assert group.members == [user]
    assert user.groups == [group]
    group.members.remove(user)
    session.flush()
    assert group.members == []
    assert user.groups == []  # This assertion fails, user.groups is [None]
Run Code Online (Sandbox Code Playgroud)

我试图通过assoc_proxy问题跟踪SQLAlchemy关系的答案,如何双向使用SQLAlchemy association_proxy?但他们似乎没有帮助.

Way*_*ner 2

我几乎完全偶然发现了你的问题,因为我试图弄清楚发生了什么。

因为数据库中没有任何数据,所以我添加了一个session.commit(). 事实证明(来自链接的答案):

在数据库收到当前事务的 COMMIT(这就是 session.commit() 的作用)之前,更改不会永久保留到磁盘,也不会对其他事务可见。

因为您只是.flush()进行更改,所以 sqlalchemy 永远不会重新查询数据库。您可以通过添加以下内容来验证这一点:

import logging
logging.getLogger('sqlalchemy').setLevel(logging.INFO)
logging.getLogger('sqlalchemy').addHandler(logging.StreamHandler())
Run Code Online (Sandbox Code Playgroud)

然后只需运行您的代码即可。它将显示发生时运行的所有查询。然后你可以更改session.flush()session.commit()然后重新运行,你会看到SELECT在你的commit.

看起来session.expire(user)session.refresh(user)也会强制用户刷新。我不确定是否有一种方法可以在不明确的情况下强制更新传播到另一个对象(或者是否需要这样做)。