在 SQLAlchemy 中引用同一表列的多个外键

Posted

技术标签:

【中文标题】在 SQLAlchemy 中引用同一表列的多个外键【英文标题】:Multiple foreign keys referencing same table column in SQLAlchemy 【发布时间】:2021-07-02 15:36:12 【问题描述】:

我正在创建一个评级系统。评级是一个表格,其中包含单个评级作为行。每个评级都有一个“评级者”和一个“评级者”。这两列通过外键引用不同的表“用户”。但是,它们都引用了相同的 user.id 列。代码:

class Rating(db.Model):
  id = db.Column(db.Integer, primary_key=True)
  rater_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
  ratee_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)

下面是它们在 User 类(表)中的表示方式:

class User(db.Model, UserMixin):
  id = db.Column(db.Integer, primary_key=True)
  username = db.Column(db.String(32), unique=True, nullable=False)
  ratesOfOthers = db.relationship('Rating', backref='rater', lazy=True)
  ratingsByOthers = db.relationship('Rating', backref='ratee', lazy=True)

现在,当我尝试使用这种关系时,出现以下错误:

sqlalchemy.exc.AmbiguousForeignKeysError:无法确定关系 User.ratesOfOthers 上的父/子表之间的连接条件 - 有多个外键路径链接表。指定 'foreign_keys' 参数,提供应计为包含对父表的外键引用的列的列表。

我尝试在 User 类中使用 foreign_keys 参数,但没有任何效果。任何帮助将不胜感激。

【问题讨论】:

【参考方案1】:

假设在您的场景中,用户可以对其他用户进行评分,而他们(他们自己)也可以被评分。基本上,有一个名为User 的表引用应用程序中的其他用户。

将一个类的实例链接到同一类的其他实例的关系称为自引用关系,这正是您在这里所拥有的。

这是一个图表,它代表了这种跟踪评级的自引用多对多关系:

Ratings 表是关系的关联表。此表中的外键指向 User 表中的条目,因为它将用户链接到用户。

要将此表添加到您的数据库中,您可以这样做:

ratings = db.Table('ratings'
                   db.Column('my_ratings_id', db.Integer, db.ForeignKey('user.id'))
                   db.Column('other_people_rating_id', db.Integet, db.ForeignKey('user.id'))
                   )

这是一个辅助表(如上所示直接翻译),除了外键之外没有其他数据。因此,它是在没有关联模型类的情况下创建的。

要在User 表中声明多对多关系,请添加:

class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(32), unique=True, nullable=False)
    
    
    def __repr__(self):
        return f'self.username'
        
    my_ratings = db.relationship(
                                 'User',
                                 secondary=ratings,
                                 primaryjoin=(ratings.c.my_ratings_id == id),
                                 secondaryjoin=(ratings.c.other_people_rating_id == id),
                                 backref = db.backref('other_people_rating', lazy='dynamic'), lazy='dynamic'
                                 )

我正在定义从左侧用户看到的名为my_ratings 的关系,因为当您从左侧查询此关系时,您将获得右侧所有这些关系的列表。从视觉上看,这就是我的意思:

检查db.relationship() 调用的所有参数,您会看到:

User 是关系的右侧实体。 secondary 配置ratings 关联表 primaryjoin 表示将左侧实体与关联表链接的条件。 user id 应该匹配 my_ratings_id secondaryjoin 表示将右侧实体与关联表链接的条件。同样,other_people_rating_id 应该匹配 user id backref 定义如何从右侧实体访问此关系。从左侧开始,关系命名为my_ratings,所以从右侧开始,我决定将其命名为other_people_rating,以代表与右侧目标用户链接的所有左侧用户。 dynamic 模式用于将查询设置为在明确请求之前不运行。 第二个lazy 参数适用于左侧查询而不是右侧。

【讨论】:

这是一个很好的答案。谢谢你的详尽解释,我学到了很多。

以上是关于在 SQLAlchemy 中引用同一表列的多个外键的主要内容,如果未能解决你的问题,请参考以下文章

sqlalchemy中复合外键的多个关系

SQLAlchemy filter_by 来自同一列的多个项目

在 Diesel 中引用同一个表的多个外键

如何使用主键作为JPA和Hibernate的外键引用?

如何使用 SQLAlchemy 核心选择除 postgresql 中的 1 个特定列之外的所有表列?

“ORA-22812: 尝试访问系统表时无法引用嵌套表列的存储表”