python - 为什么我会收到 AmbiguousForeignKeysError?
问题描述
遵循此处的 SqlAlchemy 指南后,我遇到了一个问题。
给定以下简化模块:
class _Base():
id_ = Column(Integer, primary_key=True, autoincrement=True)
Base = declarative_base(cls=_Base)
class BlgMixin():
@declared_attr
def __table_args__(cls):
return {'schema': "belgarath_backup", "extend_existing": True}
class DataAccessLayer():
def __init__(self):
conn_string = "mysql+mysqlconnector://root:root@localhost/"
self.engine = create_engine(conn_string)
def create_session(self):
Base.metadata.create_all(self.engine)
Session = sessionmaker()
Session.configure(bind=self.engine)
self.session = Session()
class Player(Base, BlgMixin):
__tablename__ = "player"
name_ = Column(String(100))
match = relationship("MatchResult")
class MatchResult(Base, BlgMixin):
__tablename__ = "match_result"
p1_id = Column(Integer, ForeignKey(f"{BlgMixin.__table_args__.get('schema')}.player.id_"))
p2_id = Column(Integer, ForeignKey(f"{BlgMixin.__table_args__.get('schema')}.player.id_"))
p1 = relationship("Player", foreign_keys=f"{BlgMixin.__table_args__.get('schema')}.player.id_")
p2 = relationship("Player", foreign_keys=f"{BlgMixin.__table_args__.get('schema')}.player.id_")
我正在尝试使用以下方法构建查询:
dal = DataAccessLayer()
dal.create_session()
player_1 = aliased(Player)
player_2 = aliased(Player)
matches = dal.session.query(MatchResult.p1_id, player_1.name_, MatchResult.p2_id, player_2.name_)
matches = matches.join(player_1)
matches = matches.join(player_2)
为什么我会收到以下错误?
Could not determine join condition between parent/child tables on relationship Player.match - there are multiple foreign key paths linking the tables. Specify the 'foreign_keys' argument, providing a list of those columns which should be counted as containing a foreign key reference to the parent table.
我很确定我指定了两个外键关系?
更新:
我已经尝试了以下组合,因为我认为评论中已建议但得到了相同的错误:
p1 = relationship("Player", foreign_keys=[p1_id])
p2 = relationship("Player", foreign_keys=[p2_id])
更新 2:
添加了一些关于输出应该是什么样子的细节:
player
桌子:
+-----+-------+
| id_ | name_ |
+-----+-------+
| 1 | foo |
| 2 | bar |
| 3 | baz |
| 4 | zoo |
+-----+-------+
match_result
桌子:
+-----+-------+-------+
| id_ | p1_id | p2_id |
+-----+-------+-------+
| 1 | 1 | 2 |
| 2 | 2 | 1 |
| 3 | 3 | 1 |
| 4 | 1 | 4 |
+-----+-------+-------+
查询输出:
+-------+---------+-------+---------+
| p1_id | p1_name | p2_id | p2_name |
+-------+---------+-------+---------+
| 1 | foo | 2 | bar |
| 2 | bar | 1 | foo |
| 3 | baz | 1 | foo |
| 1 | foo | 4 | zoo |
+-------+---------+-------+---------+
解决方案
问题在于类的这种关系的match = relationship("MatchResult")
定义Player
。如果您完全删除此行,并为关系使用以下定义,则您提到的所有查询都应按预期工作:
class Player(Base, BlgMixin):
__tablename__ = "player"
name_ = Column(String(100))
class MatchResult(Base, BlgMixin):
__tablename__ = "match_result"
p1_id = Column(ForeignKey(Player.id_))
p2_id = Column(ForeignKey(Player.id_))
p1 = relationship(Player, foreign_keys=p1_id)
p2 = relationship(Player, foreign_keys=p2_id)
实际上,也可以构造所需的选择查询,但您需要在 JOIN 上明确指定关系:
player_1 = aliased(Player)
player_2 = aliased(Player)
q = (
dal.session
.query(
MatchResult.p1_id,
player_1.name_,
MatchResult.p2_id,
player_2.name_,
)
.join(player_1, MatchResult.p1) # explicitly specify which relationship/FK to join on
.join(player_2, MatchResult.p2) # explicitly specify which relationship/FK to join on
)
但是,我会对该模型进行更多更改,以使其更加用户友好:
- 添加
backref
到关系中,以便可以从Player
- 添加一个
property
以显示双方一名球员的所有比赛
模型定义:
class Player(Base, BlgMixin):
__tablename__ = "player"
name_ = Column(String(100))
@property
def all_matches(self):
return self.matches_home + self.matches_away
class MatchResult(Base, BlgMixin):
__tablename__ = "match_result"
p1_id = Column(ForeignKey(Player.id_))
p2_id = Column(ForeignKey(Player.id_))
p1 = relationship(Player, foreign_keys=p1_id, backref="matches_home")
p2 = relationship(Player, foreign_keys=p2_id, backref="matches_away")
这将允许按照以下示例导航关系:
p1 = session.query(Player).get(1)
print(p1)
for match in p1.all_matches:
print(" ", match)
推荐阅读
- android - Android“绕过用户批准”第二次以编程方式连接到特定 Wi-Fi 网络不起作用
- gitlab - 根据条件在 GitLab 中跳过 YAML 文件中的块
- mongodb - 如何使用 mongodb 中两个集合的聚合计算利润?
- python - 如何在使用 R markdown 时将 Python 代码的输出拟合到 pdf 中?
- stenciljs - StencilJS:可以在@State 上使用@Watch
- php - 为什么我的数据没有提交到数据库中
- mysql - SQL 错误 (1093) 您不能在 FROM 子句中指定要更新的目标表和子查询
- java - 连接Android Auto后RecyclerView ViewHolders消失
- java - JFrame - 使用 JSlider 更改正在播放的音频文件的音量
- python - googletrans 几分钟后超时它会很好地工作,googletrans 每天限制时间吗?