Sqlalchemy多对多关系:只有在没有更多引用的情况下才删除子项?

时间:2015-11-28 05:03:40

标签: python sqlalchemy

我正在编写一个简单的书签管理器程序,它使用SQLAlchemy进行数据存储。我有书签和标签的数据库对象,它们之间有多对多的关系:书签可以使用数据库中的任何标签,每个标签可以分配给数据库中的任何(甚至所有)书签。程序会自动创建和删除标签 - 如果引用标签的书签数量降至零,则应删除标签。

这是我的模型代码,其中包含不必要的方法,例如__str__()已删除:

mark_tag_assoc = Table('mark_tag_assoc', Base.metadata,
        Column('mark_id', Integer, ForeignKey('bookmarks.id')),
        Column('tag_id', Integer, ForeignKey('tags.id')),
        PrimaryKeyConstraint('mark_id', 'tag_id'))

class Bookmark(Base):
    __tablename__ = 'bookmarks'

    id = Column(Integer, primary_key=True)
    name = Column(String)
    url = Column(String)
    description = Column(String)
    tags_rel = relationship("Tag", secondary=mark_tag_assoc,
                            backref="bookmarks", cascade="all, delete")

class Tag(Base):
    __tablename__ = 'tags'

    id = Column(Integer, primary_key=True)
    text = Column(String)

我认为,如果我设置了一个级联(cascade="all, delete"),它将会删除标签而不再为我提供参考。实际发生的是,当删除任何书签时,它引用的所有标签都会自动从所有其他书签中删除并删除,这显然不是预期的行为。

是否有一个简单的选项可以做我想要的,或者如果不是,那么自己实施它的最简洁方法是什么?虽然我对简单的SQL有一点经验,但这是我第一次使用SQLAlchemy,所以详情请参阅。

1 个答案:

答案 0 :(得分:1)

我仍然有兴趣知道是否有相应的内置功能,但经过进一步的研究后,我似乎更有可能没有,因为通常似乎没有太多用于执行具有多对多关系的复杂内容的有用功能。这就是我解决它的方法:

  1. 从关系中删除cascade="all, delete",以便不执行级联。即使没有配置级联,SQLAlchemy仍会在删除书签时从关联表中删除行。
  2. 每次删除书签后调用一个函数来检查标签是否仍有任何关系,如果没有则删除标签:
  3. def maybeExpungeTag(self, tag):
        """
        Delete /tag/ from the tags table if it is no longer referenced by
        any bookmarks.
    
        Return:
            True if the tag was deleted.
            False if the tag is still referenced and was not deleted.
        """
        if not len(tag.bookmarks):
            self.session.delete(tag)
            return True
        else:
            return False
    
    # and for the actual delete...
    mark = # ...get the bookmark being deleted
    tags = mark.tags_rel
    self.session.delete(mark)
    for tag in tags:
        self.maybeExpungeTag(tag)
    self.session.commit()