【问题标题】:sqlachemy Many to Many: remove is not working and delete is too muchsqlalchemy 多对多:删除不起作用,删除太多
【发布时间】:2020-04-12 10:39:36
【问题描述】:

我正在做一个项目,包括链接两个类:用户和组。
一个用户可以在一些组中,每个组可以有一些用户。

所以,我创建了第三个表:group_guest,用于保存 user_id 和 group_id。
当我创建 user1、user2、group1 和 group2 时,我可以将 group1 和 group2 添加到 user1。
并将 group1 添加到 user2

问题:那么我不能只从 user1 中删除 group1 并且删除 group1 不好:user2 没有更多的组:/

我几乎尝试了所有组合:级联、反向引用、删除孤儿...
如果有人作为一个想法......我应该修改模型吗?
我将不胜感激!

代码:

from app import db  
from flask_login import UserMixin  

group_guest = db.Table('group_guest',  
                       db.Column('user_id', db.Integer, db.ForeignKey('user.id')),  
                       db.Column('group_id', db.Integer, db.ForeignKey('group.id'))  
                       )


class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), index=True, unique=True)

    group_guest_group_id = db.relationship(
        "Group",
        secondary=group_guest,
        back_populates="group_guest_user_id",
        cascade="all, delete",
        lazy='dynamic'
    )


class Group(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    entitled = db.Column(db.String(64))

    group_guest_user_id = db.relationship(
        "User",
        secondary=group_guest,
        back_populates="group_guest_group_id",
        lazy='dynamic'
    )

然后:

user1.group_guest_group_id.remove(group1)
db.session.commit()
user1.guested_group().all()

它应该(在我看来)只返回 group2 但它返回两个

【问题讨论】:

    标签: python database sqlalchemy many-to-many flask-sqlalchemy


    【解决方案1】:

    您似乎希望在整个表中建立不对称关系:

    • 如果您删除 User,那么显然所有对 User 的二级引用也应删除,即在二级表中级联。
    • 但如果组中至少存在一个User,您不希望能够删除Group,删除活动Group 会引发完整性错误。

    在这种情况下,您可以尝试以下方法:

    group_guest = db.Table('group_guest',  
                       db.Column('user_id', db.Integer, db.ForeignKey('user.id', ondelete='CASCADE'), primary_key=True),  
                       db.Column('group_id', db.Integer, db.ForeignKey('group.id', ondelete='RESTRICT'), primary_key=True)
                       )
    
    class User(UserMixin, db.Model):
        id = db.Column(db.Integer, primary_key=True)
        username = db.Column(db.String(64), index=True, unique=True)
    
        group_guest_group_id = db.relationship("Group",
            secondary=group_guest,
            back_populates="group_guest_user_id",
            passive_deletes='all', lazy='dynamic'
        )
    
    
    class Group(db.Model):
        id = db.Column(db.Integer, primary_key=True)
        entitled = db.Column(db.String(64))
    
        group_guest_user_id = db.relationship("User",
            secondary=group_guest,
            back_populates="group_guest_group_id",
            passive_deletes='all', lazy='dynamic'
        )
                      
    

    被动删除可防止 SQLAlchemy 自动执行级联删除,并且由 foreignKey 的“ondelete”指定的数据库低级指令直接指示操作。

    由于辅助表上的主键不能为 NULL,因此无论如何您都不能删除父表 Group。我只是喜欢 ondelete='Restrict' 行,因为它明确地说明了规则,并且如果您以后重新访问它可以更容易维护/调试。

    已编辑

    以下对我有用,没有问题:

    from sqlalchemy import *
    from sqlalchemy.orm import *
    from sqlalchemy.ext.declarative import declarative_base
    import uuid
    
    Base= declarative_base()
    
    group_guest= Table('group_guest', Base.metadata,
        Column('user_id', Integer, ForeignKey('user.id', ondelete='CASCADE'), primary_key=True),
        Column('group_id', Integer, ForeignKey('group.id', ondelete='RESTRICT'), primary_key=True))
    
    class User(Base):
        __tablename__ = 'user'
        id = Column(Integer, primary_key=True)
        username = Column(String(64), index=True, unique=True)
    
        group_guest_group_id = relationship("Group",
            secondary=group_guest,
            back_populates="group_guest_user_id",
            passive_deletes='all', lazy='dynamic'
        )
    
    class Group(Base):
        __tablename__ = 'group'
        id = Column(Integer, primary_key=True)
        entitled = Column(String(64))
    
        group_guest_user_id = relationship("User",
            secondary=group_guest,
            back_populates="group_guest_group_id",
            passive_deletes='all', lazy='dynamic'
        )
    
    
    e = create_engine("sqlite://")
    Base.metadata.create_all(e)
    
    s = Session(e)
    
    u1 = User(username='A')
    u2 = User(username='B')
    g1 = Group()
    g2 = Group()
    s.add_all([u1, u2, g1, g2])
    s.commit()
    
    u1.group_guest_group_id.append(g1)
    u1.group_guest_group_id.append(g2)
    g1.group_guest_user_id.append(u2)
    g2.group_guest_user_id.append(u2)
    
    s.add_all([u1, u2, g1, g2])
    s.commit()
    
    u1.group_guest_group_id.remove(g1)
    s.add(u1)
    s.commit()
    
    print([group for group in u1.group_guest_group_id])
    print([group for group in u2.group_guest_group_id])
    

    【讨论】:

    • 嗨 Attack68,感谢您的洞察力,限制更合乎逻辑!但是“user1.group_guest_group_id.remove(group1)”仍然无法从 user1 中删除 group1。 ("user1.guested_group().all()" 仍然等于 [bet1, bet2]) 同时,如果我再次 remove() group1,我得到一个错误:“exc.StaleDataError:DELETE statement on table 'bet_guest ' 预计删除 1 行;只有 0 行匹配“您有其他想法我愿意尝试!
    • 我添加的代码对我来说没有错误,所以除了缺少add() session 命令之外,你的似乎相同。
    • 是的,我的函数 guested_group() 也很糟糕,感谢您的帮助!
    • @oliv7 如果它解决了问题并且有效,请接受答案!
    猜你喜欢
    • 2010-10-18
    • 2021-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-05
    • 2021-10-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多