我使用双向association_proxy
来关联属性Group.members
和User.groups
。我有从Group.members
移除成员的问题。特别地,Group.members.remove
将成功地从Group.members
中删除一个条目,但会在User.groups
中留下一个None
来代替相应的条目。更具体地说,以下(最小的)代表性代码片段没有通过其最后一个断言:
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]
我已经尝试遵循答案SQLAlchemy关系与association_proxy问题和SQLAlchemy association_proxy如何被双向使用?
我发现你的问题几乎完全是偶然的,当我试图弄清楚发生了什么。
因为数据库中没有任何数据,所以我添加了一个session.commit()
。结果是(从链接的答案中):
更改不会永久保存到磁盘,也不会对其他事务可见,直到数据库接收到当前事务的COMMIT(这是session.commit()所做的)。
因为您只是.flush()
更改,sqlalchemy永远不会重新查询数据库。您可以通过添加
import logging
logging.getLogger('sqlalchemy').setLevel(logging.INFO)
logging.getLogger('sqlalchemy').addHandler(logging.StreamHandler())
然后简单地运行代码。它将显示正在运行的所有查询。然后您可以将session.flush()
更改为session.commit()
,然后重新运行,您将看到几个SELECT
语句在commit
之后运行。
看起来session.expire(user)
或session.refresh(user)
也会强制用户刷新。我不确定是否有一种方法可以强制更新传播到另一个对象而不显式地(或者如果这甚至是可取的)。