如何使用 SQLAlchemy 2.0 语法中包含别名表联接的查询删除记录?

Jos*_*ssy 6 python sqlalchemy

我正在尝试根据包含与几个别名表的联接的查询删除记录。

以下是有问题的表格:

class Match(Base):
    
    id_ = Column(Integer, primary_key=True)
    tournament_id = Column(Integer, ForeignKey("myschema.tournament.id_"))
    round_id = Column(TINYINT, index=True)
    player_id_p1 = Column(Integer, ForeignKey("myschema.player.id_"))
    player_id_p2 = Column(Integer, ForeignKey("myschema.player.id_"))

    p1 = relationship("Player", foreign_keys=[player_id_p1])
    p2 = relationship("Player", foreign_keys=[player_id_p2])


class Tournament(Base):

    id_ = Column(Integer, primary_key=True)
    original_id = Column(Integer, index=True)
    tour_id = Column(TINYINT, index=True)

    match = relationship("Match", backref="tournament")


class Player(Base):

    id_ = Column(Integer, primary_key=True)
    original_id = Column(Integer, index=True)
    tour_id = Column(TINYINT, index=True)

    match = relationship(
        'Match',
        primaryjoin=("or_(Player.id_ == Match.player_id_p1, Player.id_ == Match.player_id_p2)"),
        overlaps="p1, p2",
    )
Run Code Online (Sandbox Code Playgroud)

值得一提的是,这些表格是从第三方数据库填充的,该数据库包含来自两个网球巡回赛的锦标赛、球员和比赛;ATP 和 WTA。在该数据库中,每个巡回赛都有单独的锦标赛、球员和比赛表。我已将它们导入数据库中的组合表中,并使用一个tour_id字段来标识它们最初来自哪个旅行/表。我需要能够Match根据原始锦标赛和玩家 ID 从表中删除记录。

我首先尝试了这个查询:

p1 = sa.orm.aliased(Player)
p2 = sa.orm.aliased(Player)
stmt = sa.delete(Match)
stmt = stmt.join(Tournament)
stmt = stmt.join(p1, p1.id_ == Match.player_id_p1)
stmt = stmt.join(p2, p2.id_ == Match.player_id_p2)
stmt = stmt.where(
    Tournament.tour_id == tour_id,
    Tournament.original_id == 16907,
    p1.tour_id == tour_id,
    p1.original_id == 79810,
    p2.tour_id == tour_id,
    p2.original_id == 37136,
    Match.round_id == 5,
)
session.execute(stmt)
Run Code Online (Sandbox Code Playgroud)

但是,我收到错误:

'Delete' object has no attribute 'join'
Run Code Online (Sandbox Code Playgroud)

这个相关的答案指出,在 1.x 语法中,SA 将从内部获取表filter并转换为USINGSQL。由此我用 2.0 语法构建了以下查询:

p1 = sa.orm.aliased(Player)
p2 = sa.orm.aliased(Player)
stmt = sa.delete(Match)
stmt = stmt.where(
    Tournament.tour_id == 0,
    Tournament.original_id == 16907,
    p1.tour_id == 0,
    p1.original_id == 79810
    p2.tour_id == 0,
    p2.original_id == 37136,
    Match.round_id == 5,
)
session.execute(stmt)
Run Code Online (Sandbox Code Playgroud)

但是,我随后收到错误:

Exception has occurred: InvalidRequestError       (note: full exception trace is shown but execution is paused at: <module>)
Could not evaluate current criteria in Python: "Can't evaluate criteria against alternate class <class 'Tournament'>". Specify 'fetch' or False for the synchronize_session execution option.
Run Code Online (Sandbox Code Playgroud)

我不确定建议的操作会产生什么效果,所以我也在这里调整了解决方案,如下所示:

p1 = sa.orm.aliased(Player)
p2 = sa.orm.aliased(Player)
s_qry = sa.select(Match.id_)
s_qry = s_qry.join(Tournament)
s_qry = s_qry.join(p1, p1.id_ == Match.player_id_p1)
s_qry = s_qry.join(p2, p2.id_ == Match.player_id_p2)
s_qry = s_qry.where(
    Tournament.tour_id == tour_id,
    Tournament.original_id == 16907,
    p1.tour_id == tour_id,
    p1.original_id == 79810,
    p2.tour_id == tour_id,
    p2.original_id == 37136,
    Match.round_id == 5,
)
s_qry = s_qry.subquery()
stmt = sa.delete(Match).where(Match.id_.in_(s_qry))
session.execute(stmt)
Run Code Online (Sandbox Code Playgroud)

但我现在收到错误:

Exception has occurred: InvalidRequestError       (note: full exception trace is shown but execution is paused at: <module>)
Could not evaluate current criteria in Python: "Cannot evaluate Select". Specify 'fetch' or False for the synchronize_session execution option.
Run Code Online (Sandbox Code Playgroud)

可能值得一提的是,在这种情况下,没有与查询条件相对应的记录。

实现我想要做的事情的最佳方法是什么?

hyi*_*yit 1

AFAIK,whereSQLAlchemy 中的子句是单个语句操作。要考虑多个条件,您需要使用and_运算符,或链接多个.where调用。

换句话说,我相信这可以解决您的问题 -

  1. 使用and_运算符:
stmt = stmt.where(and_(Tournament.tour_id == tour_id,
                       Tournament.original_id == 16907,
                       p1.tour_id == tour_id,
                       p1.original_id == 79810,
                       p2.tour_id == tour_id,
                       p2.original_id == 37136,
                       Match.round_id == 5))
Run Code Online (Sandbox Code Playgroud)
  1. 链接多个.where调用:
stmt = stmt.where(Tournament.tour_id == tour_id).\
            where(Tournament.original_id == 16907).\
            where(p1.tour_id == tour_id).\
            where(p1.original_id == 79810).\
            where(p2.tour_id == tour_id).\
            where(p2.original_id == 37136).\
            where(Match.round_id == 5)
Run Code Online (Sandbox Code Playgroud)