SQLAlchemy:对象列表,如果删除了,则保留引用

2024-10-02 14:24:18 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图实现一个面向用户的文章预览列表,即使删除了Article,它也会保持其大小。因此,如果列表有四个对象[1, 2, 3, 4]并且其中一个被删除,我希望它包含[1, 2, None, 4]。在

我使用的是secondary表的关系。目前,删除ArticlePreviewList将删除该表中的行。我尝试过级联选项,但它们似乎直接影响相关项,而不是secondary表的内容。在

下面的代码片段测试所需的行为:删除Article应该保留ArticlePreviewListAssociation中的行,但是删除PreviewList应该删除它(而不是{})。在

在下面的代码中,删除Article将保留ArticlePreviewListAssociation,但是pl.articles不会将其视为列表项。在

from db import DbSession, Base, init_db
from sqlalchemy import Column, String, Integer, ForeignKey
from sqlalchemy.orm import relationship

session = DbSession()


class Article(Base):
    __tablename__ = 'articles'
    id = Column(Integer, primary_key=True)
    title = Column(String)


class PreviewList(Base):
    __tablename__ = 'preview_lists'
    id = Column(Integer, primary_key=True)
    articles = relationship('Article', secondary='associations')


class ArticlePreviewListAssociation(Base):
    __tablename__ = 'associations'
    article_id = Column(Integer, ForeignKey('articles.id'), nullable=True)
    previewlist_id = Column(Integer, ForeignKey('preview_lists.id'), primary_key=True)

    article = relationship('Article')
    preview_list = relationship('PreviewList')


init_db()

print(f"Creating test data")
a = Article(title="StackOverflow: 'Foo' not setting 'Bar'?")
pl = PreviewList(articles=[a])
session.add(a)
session.add(pl)
session.commit()

print(f"ArticlePreviewListAssociations: {session.query(ArticlePreviewListAssociation).all()}")

print(f"Deleting PreviewList")
session.delete(pl)
associations = session.query(ArticlePreviewListAssociation).all()
print(f"ArticlePreviewListAssociations: should be empty: {associations}")
if len(associations) > 0:
    print("FAIL")

print("Reverting transaction")
session.rollback()

print("Deleting article")
session.delete(a)
articles_in_list = pl.articles
associations = session.query(ArticlePreviewListAssociation).all()
print(f"ArticlePreviewListAssociations: should not be empty: {associations}")
if len(associations) == 0:
    print("FAIL")
print(f"Articles in PreviewList: should not be empty: {articles_in_list}")
if len(articles_in_list) == 0:
    print("FAIL")
# desired outcome: pl.articles should be [None], not []

print("Reverting transaction")
session.rollback()

这可以归结为“你如何建立一个多对多的关系,其中pk_A == 1 and pk_B == NULL包含在a的列表中在


Tags: idtrue列表basesessionarticlecolumninteger
1条回答
网友
1楼 · 发布于 2024-10-02 14:24:18

所举的例子似乎假定相关条款的顺序是保持不变的,即使在删除时也是如此。有多种方法可以实现这一点,例如Ordering List扩展,但是首先解决与已删除文章的关联的问题更容易。这看起来像是association object和{a3}的用例。在

Article类获得一个新的关系,以便在会话中级联删除。默认的ORM-level cascading行为是将外键设置为NULL,但是如果没有加载相关的关联对象,我们希望让数据库执行此操作,因此使用passive_deletes=True

class Article(Base):
    __tablename__ = 'articles'

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

    previewlist_associations = relationship(
        'ArticlePreviewListAssociation', back_populates='article',
        passive_deletes=True)

{many-association.{many-association-to-many-association-of-many-object-to-many-association-of-many-object-to-association-of-many-object-to-association-of-many-object-to-association,而不是。这一次级联有点不同,因为如果父对象PreviewList被删除,则应该删除关联对象:

^{pr2}$

最初,association对象使用previewlist_id作为主键,但是PreviewList只能包含一个Article。代理键解决了这个问题。外键配置包括DB级级联。以下是使用被动删除的原因:

class ArticlePreviewListAssociation(Base):
    __tablename__ = 'associations'

    id = Column(Integer, primary_key=True)
    article_id = Column(
        Integer, ForeignKey('articles.id', ondelete='SET NULL'))
    previewlist_id = Column(
        Integer, ForeignKey('preview_lists.id', ondelete='CASCADE'),
        nullable=False)

    # Using a unique constraint on a nullable column is a bit ugly, but
    # at least this prevents inserting an Article multiple times to a
    # PreviewList.
    __table_args__ = (UniqueConstraint(article_id, previewlist_id), )

    article = relationship(
        'Article', back_populates='previewlist_associations')
    preview_list = relationship(
        'PreviewList', back_populates='article_associations')

有了这些更改,就不会打印“失败”。在

相关问题 更多 >