首页 > 解决方案 > sqlachemy 多对多:删除不起作用,删除太多

问题描述

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

因此,我创建了第三个表: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 但它返回两者

标签: pythondatabasesqlalchemymany-to-manyflask-sqlalchemy

解决方案


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

  • 如果您删除 aUser那么显然所有对它的user-group辅助引用也User应该被删除,即级联在辅助表中。
  • Group但是如果组中至少存在一个,您不希望能够删除一个User,删除一个活动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])

推荐阅读