【问题标题】:Rails has_many through a HABTM relationshipRails has_many 通过 HABTM 关系
【发布时间】:2021-06-26 18:55:21
【问题描述】:

所以我有三个模型,用户、团队和游戏。目前是这样构建的。

class Team < ApplicationRecord
    has_and_belongs_to_many :users
    has_many :home_games, class_name: 'Game', foreign_key: 'home_team_id'
    has_many :away_games, class_name: 'Game', foreign_key: 'away_team_id'
    has_many :wins, class_name: 'Game', foreign_key: 'winner_id'
    belongs_to :owner, class_name: 'User'
end
class User < ApplicationRecord
  has_and_belongs_to_many :teams
  has_many :teams_owned, class_name: 'Team', foreign_key: 'owner_id'
  has_many :games, through: :teams
end
class Game < ApplicationRecord
  belongs_to :home_team, class_name: "Team"
  belongs_to :away_team, class_name: "Team"
  belongs_to :winner, class_name: "Team", optional: true
end

我想在用户和游戏之间添加关联。所以我可以调用 User.games 和 Game.users。

我尝试添加这个:

#in user model
has_many :games, through: :teams

#in team model
has_many :games, ->(team) { where('home_team_id = ? or away_team_id = ?', team.id, team.id) }, class_name: 'Game'

正如api docs 所说的那样。但是,当我尝试调用此关联时,我收到“game.team_id 不存在”的错误消息。因为每场比赛都有 home_team_id 和 away_team_id,但没有 team_id。

我只是执行得非常糟糕吗?还是我错过了什么?任何帮助表示赞赏。

【问题讨论】:

  • 哪一行出现错误?
  • @JoelBlum 当我尝试调用 User.games 时

标签: ruby-on-rails


【解决方案1】:

我会说这不是一个很好的解决方案。

在 ActiveRecord 中,您实际上无法定义外键可能位于两个不同列中的关联,如下所示:

has_many :games, ->(team) { where('home_team_id = ? or away_team_id = ?', team.id, team.id) }, class_name: 'Game'

它肯定行不通,因为 Rails 仍会以JOIN games ON games.team_id = teams.id 的身份加入该协会。仅仅在查询中添加一个WHERE 子句并不能解决这个问题。由于 ActiveRecord 实际上会创建各种不同的查询,因此无法简单地提供不同的连接。

要完成这项工作,需要添加一个实例方法:

class Game < ApplicationRecord
  def users
    User.joins(:teams)
        .where(teams: { id: home_team.id })
        .or(Team.where(id: away_team.id))
  end
end

由于它不是一个实际的关联,因此您不能通过它加入或使用某种急切加载来避免 n+1 查询。

如果您确实想创建一个可以加入的关联,则需要在游戏和团队之间添加一个连接表。

class Team < ApplicationRecord
  # ...
  has_many :game_teams
  has_many :games, through: :game_teams
end

# rails g model game_team team:belongs_to game:belongs_to score:integer 
class GameTeam < ApplicationRecord
  belongs_to :team
  belongs_to :game
end

class Game < ApplicationRecord
  has_many :game_teams
  has_many :teams, through: :game_teams
  has_many :users, through: :teams
end

这是一个更好的主意,因为它为您提供了一个合理的位置来记录每支球队的得分。

顺便说一句,如果球队的组成可以改变并且准确的记录保存很重要,那么您实际上可能需要额外的连接表,因为比赛进行时的阵容可能实际上与当前阵容不匹配。

【讨论】:

  • 第二个注意事项,HABTM 毫无用处。您几乎总是想使用has_many through:flatironschool.com/blog/…
  • 团队的组成可以改变。但是,我不需要跟踪历史比赛的团队组成。我还是想明确的知道谁是主队和客队。最好的解决方案是将“is_home_team”列添加到 GameTeam 连接表吗?这就是我现在的倾向。
  • 是的,这是一个选项。我会在 PG 上使用枚举列而不是布尔值,因为查询 SELECT game_teams WHERE played_at = 'away' 而不是 is_home_team = 0 更有意义。
  • 是的。您需要一个包含 user_id、team_id 和 game_id 列的三重连接表。
  • 这取决于如何查询/使用它。如果您的每支球队有大量球员,而其中许多球员缺席了每场比赛,您可能希望使用两张桌子,而不是只使用一张上面有花旗的桌子。
猜你喜欢
  • 2015-07-04
  • 2012-06-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多