SQLAlchemy:直接从一对多关系中删除对象,而不使用 session.delete()

Posted

技术标签:

【中文标题】SQLAlchemy:直接从一对多关系中删除对象,而不使用 session.delete()【英文标题】:SQLAlchemy: Delete object directly from one-to-many relationship without using session.delete() 【发布时间】:2014-06-12 23:42:18 【问题描述】:

我有以下 SQLAlchemy 设置:

Base = declarative_base()

class Post(Base):
    __tablename__ = 'post'
    id = Column(Integer, primary_key=True)
    title = Column(String(30))
    comments = relationship('Comment', cascade='all')

class Comment(Base):
    __tablename__ = 'comment'
    id = Column(Integer, primary_key=True)
    post_id = Column(Integer, ForeignKey(Post.id, ondelete='CASCADE'), nullable=False)
    text = Column(Text)

有了这个,我可以创建与 cmets 具有一对多关系的帖子对象。我想在不引用会话的情况下处理帖子的 cmets 的创建和删除。在帖子中添加评论就可以了:

post = Post(title='some post')
comment = Comment(text='some comment')
post.comments.append(comment)

我的会话处理程序只知道帖子,所以它会执行session.add(post),并且评论会自动放入会话中,并在下一个session.commit() 上与数据库同步。但是,对于 cmets 的删除,情况并非如此。我希望能够通过以下方式删除评论:

post.comments.remove(comment)

但是,这会在下一个session.commit() 上产生以下错误:

sqlalchemy.exc.OperationalError: (OperationalError) (1048, "Column 'post_id' cannot be null") 'UPDATE comment SET post_id=%s WHERE comment.id = %s' (None, 1L)

如何告诉 SQLAlchemy 不要使用 post_id 的 NULL 值更新评论(由于列上的非空约束,这是不允许的),而是删除评论?我知道我可以这样做 session.delete(comment),但由于我不需要显式将评论添加到会话中,我看不出为什么我必须从会话中显式删除它。

我找到了几种级联删除相关对象的解决方案,但由于我从未对会话发出任何显式删除(帖子仍然存在),我认为这不适用。

编辑:我调整了示例以包括从帖子中删除的级联。现在它可以做session.delete(post) 并且所有的cmets 都被删除了。但我只想自动删除我从关系中删除的 cmets,而不是删除包含所有 cmets 的整个帖子。

TL;DR:当我从一对多关系的关系列表中删除条目时,如何告诉 SQLAlchemy 发出删除语句而不是更新语句?

【问题讨论】:

【参考方案1】:

阅读文档的Configuring delete/delete-orphan Cascade 部分以获取更多信息,但基本上您在relationshipcascade 选项中也需要delete-orphan

class Post(Base):
    # ...
    comments = relationship('Comment', cascade="all, delete-orphan")

【讨论】:

使用级联中的delete-orphan 选项,从关系列表中删除时的删除按预期工作。谢谢。【参考方案2】:

我不是 SQLAlchemy 用户,但我认为您应该使用 ondelete 选项

post_id = Column(整数, ForeignKey(Post.id, ondelete="CASCADE"), nullable=False)

参见,mysql 5.6 Manual 13.6.44,外键约束

SET NULL:从父表中删除或更新行,并将子表中的外键列或列设置为NULL。 支持 ON DELETE SET NULL 和 ON UPDATE SET NULL 子句。 CASCADE:删除或更新父表中的行,并自动删除或更新子表中匹配的行。 支持 ON DELETE CASCADE 和 ON UPDATE CASCADE。两个表之间,不要定义几个ON UPDATE 作用于父表或子表中同一列的 CASCADE 子句。

并且 http://docs.sqlalchemy.org/en/rel_0_9/core/constraints.html 部分:定义外键 -> ON UPDATE 和 ON DELETE

【讨论】:

使用session.delete(post) 删除帖子时,这可能会起作用,但我没有这样做。所以没有什么可以级联到评论。我只想删除评论。

以上是关于SQLAlchemy:直接从一对多关系中删除对象,而不使用 session.delete()的主要内容,如果未能解决你的问题,请参考以下文章

Flask 学习-84.Flask-SQLAlchemy 一对多关系级联删除

Flask 学习-84.Flask-SQLAlchemy 一对多关系级联删除

SQLAlchemy ORM 一对多关系未从数据库加载所有记录

嵌套的一对多关系 sqlalchemy 过滤

如何从 SqlAlchemy 中的多对多集合中删除所有项目?

SQLAlchemy外键的使用