首页 > 解决方案 > SQLAlchemy:多个 ForeignKeyConstraint 引用相同的目标列

问题描述

我正在尝试正确定义几个都使用复合键的表之间的关系。我面临的问题是这些键中使用的一个列对所有表都是通用的。

考虑使用 sqlalchemy 编写的以下表和关系:

class Match(Base):
    __tablename__ = 'match'
    id = Column(String, primary_key=True, nullable=False)
    players = relationship('PlayerMatch', back_populates='match')
    region = Column(String, primary_key=True, nullable=False)

class Player(Base):
    __tablename__ = 'player'
    id = Column(String, primary_key=True, nullable=False)
    matches = relationship('PlayerMatch', back_populates='player')
    region = Column(String, primary_key=True, nullable=False)

class PlayerMatch(Base):
    __tablename__ = 'player_match'
    match = relationship('Match', back_populates='players')
    match_id = Column(String, primary_key=True, nullable=False)
    player = relationship('Player', back_populates='matches')
    player_id = Column(String, primary_key=True, nullable=False)
    region = Column(String, primary_key=True, nullable=False)

    __table_args__ = (
        ForeignKeyConstraint(['region', 'player_id'], ['player.region', 'player.id']),
        ForeignKeyConstraint(['region', 'match_id'], ['match.region', 'match.id']),
    )

region这对于 sqlachemy 来说显然是有冲突的,因为它不知道如何填充PlayerMatch. 它会引发以下警告:

SAWarning: relationship 'PlayerMatch.match' will copy column match.region to column player_match.region, which conflicts with relationship(s): 'PlayerMatch.match' (copies player.region to player_match.region). Consider applying viewonly=True to read-only relationships, or provide a primaryjoin condition marking writable columns with the foreign() annotation.

请注意,region对于涉及此关系的比赛和玩家,保证是相同的。照原样,它仍然有效,但给了我之前的警告。我花了一些时间尝试定义各种关系的连接条件,但我无法让它正常工作。我可以使用一种机制来简单地指示哪个region(来自Match或来自Player)实际上要持久保存在PlayerMatch.

实现这一目标的正确方法是什么?

谢谢你的帮助。

标签: pythonsqlalchemy

解决方案


解决方案是指定关系的primaryjoin属性,PlayerMatch.player以便它同时使用Player.idand Player.region,但要防止持久化regionfrom Player,如下所示:

player = relationship(
        'Player',
        back_populates='matches',
        primaryjoin="and_(Player.id == foreign(PlayerMatch.player_id), "
                    "Player.region == PlayerMatch.region)"
    )

推荐阅读