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

标签 python sqlalchemy

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

更具体地说,以下(最小)代表性代码片段未通过其最后断言:

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 relationship with association_proxy problems 的答案进行操作和 How can SQLAlchemy association_proxy be used bi-directionally?但他们似乎没有帮助。

最佳答案

我发现了 your problem几乎完全是偶然的,因为我正试图弄清楚发生了什么。

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

The changes aren't persisted permanently to disk, or visible to other transactions until the database receives a COMMIT for the current transaction (which is what session.commit() does).

因为您只是 .flush() 更改,sqlalchemy 从不重新查询数据库。您可以通过添加来验证这一点:

import logging
logging.getLogger('sqlalchemy').setLevel(logging.INFO)
logging.getLogger('sqlalchemy').addHandler(logging.StreamHandler())

然后简单地运行您的代码。它将显示所有正在运行的查询。然后你可以把session.flush()改成session.commit()然后重新运行,你会看到几个SELECT语句在您的提交之后运行。

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

关于python - 使用双向 association_proxy 时自动传播删除,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38962124/

相关文章:

python - 根据字符串列过滤分组数据框中的行

python - 解析 Openweather API Python

python - 将平面制表符分隔文件转换为 Json 嵌套结构

python - 如何在 Python 中从 XML/SOAP 中提取数据

python - 使用 SQLAlchemy,如何将一行转换为 "real"Python 对象?

python - 从可迭代的每个元素中删除单个键的优雅方法

python - 没有名为 sqlalchemy 的模块

python - 删除不用作外键的记录的通用解决方案

python - 两个 sqlalchemy 查询返回相同的结果,即使它们具有不同的过滤器

sqlalchemy - 在 fastapi 数据库 session 中使用生成器上下文管理器