Rails 3.2.2 - has_many through

5

试图重构代码以提供干净的关联

一个游戏有一个主队和一个客队

一个球队可以作为主队或客队拥有多场比赛

GAME和TEAM之间的关联是一种简单直接的HABTM,但我需要表示与GAME相关的两个TEAM中哪个是主队,哪个是客队。 我通过添加额外的字段和关联来完成,但这显然非常冗长而不是DRY。 我知道答案在“through”中,但我好像大脑短路了,无法完全理解。

基本上,我希望能够执行Game.teams(返回两个队伍的集合),Game.home_team(获取并设置一个TEAM为home_team)和Game.away_team(获取并设置一个TEAM为away_team)

很抱歉提出如此直接的查询,但它已经超出了我的掌握范围

class Game < ActiveRecord::Base
  belongs_to :home_team
  belongs_to :away_team
  has_and_belongs_to_many :teams
end

class HomeTeam < ActiveRecord::Base
  belongs_to :team
  has_one :games
end

class AwayTeam < ActiveRecord::Base
  belongs_to :team
  has_one :games
end

class Team < ActiveRecord::Base
  has_and_belongs_to_many :games
  has_many :away_teams
  has_many :home_teams
end 

所有的帮助都非常感激。
彼得

2
你真的想让HomeTeam和AwayTeam成为独立的类吗?而且,目前是分开的数据库表吗? - Chowlett
不,这只是一个临时解决方案。我认为我需要一个包含主队布尔值的games_teams表... - pshear0
1个回答

7
为了达到你想要的效果,你应该使用has_many :through而不是hatbm。在这里查看更多信息:here。简言之,好处是你可以将其他变量添加到连接表中。在你的情况下,一个名为home_team的布尔变量。
所以这是我会做的事情。首先,创建一个关联表(因为我没有太多想象力,所以我会称其为participation):
create_table :participations, do |t|
  t.integer :game_id, :null => false
  t.integer :team_id, :null => false
  t.boolean :home_team
end

正如您所见,与您的游戏团队表不同,这个表有一个 id。您可以向其添加属性。 然后,我会使用这些模型:

class Participation < ActiveRecord::Base
  belongs_to :game
  belongs_to :team
end

class Game < ActiveRecord::Base
  has_many :participations, :dependent => :destroy
  has_many :teams, :through => :participations
end

class Team < ActiveRecord::Base
  has_many :participations, :dependent => :destroy
  has_many :games, :through => :participations
end

要获取游戏的团队,您需要使用@game.teams

现在,为了获取主队和客队,请将以下方法添加到您的Game模型中:

def home_team
  self.teams.joins(:participations).where("participations.home_team IS ?", true).first
end

def away_team
  self.teams.joins(:participations).where("participations.home_team IS ?", false).first
end

然后你就可以使用@game.home_team@game.away_team了。
彼得的编辑:好的,对于mysql,您需要使用不同的where语句: self.teams.joins(:participants).where("participants.home_team = ?", true).first self.teams.joins(:participants).where("participants.home_team IS NULL").first
我可以使用“=?”,true和“!=?”,true - 或者IS NOT NULL和IS NULL。
我认为对于false,您应该尝试使用where("participants.home_team = ?", false) 好的,至少有两种设置团队的方法。 1.让用户选择主场球队。 2.假设第一支球队是主队。
如果您选择第1种方法,则应使用单选按钮让用户决定。类似于这样:
<%= label_tag :home, 'Home Team' %><br />
<%= label_tag :home_team_1, 'Team 1' %><%= radio_button_tag :home_team, 1 %>
<%= label_tag :home_team_2, 'Team 2' %><%= radio_button_tag :home_team, 2 %>

如果params[:home_team] == 1,则第一个球队是主队;如果params[:home_team] == 2,则第二个球队是主队。
如果您选择第二个选项,请在表单中添加以下内容以将球队添加到比赛中:
  <%= label_tag :name, 'Home Team' %>
  <%= text_field_tag :name, nil, :name => "home[]" %>

  <%= label_tag :name, 'Away Team' %>
  <%= text_field_tag :name, nil, :name => "away[]" %>

那么在你的控制器中,你可以这样做:
@game = Game.new(params[:game])

home = Team.create(params[:home])
# or
home = Team.find_or_create_by_name(params[:home][:name])
@game.participations.create(:team_id => home.id, :home_team => true or 1)

away = Team.find_or_create_by_name(params[:away][:name])
@game.participations.create(:team_id => away.id, :home_team => false or 0)

Ashitaka - 听起来非常有前途。我正在构建我的测试平台,一旦完成会告诉你结果并接受。感谢您的回复。 - pshear0
我编辑了我的问题,并且这次确实进行了测试。它正在正确运行,请尝试并告诉我结果如何! - Ashitaka
嗨,Ashitaka,我还在解决这个问题。使用你的解决方案时,我遇到了 MySql 的问题,不得不对 where 子句进行了一些更改。 - pshear0
糟糕 - 我还在适应 Stack。我将在答案底部放置我的更改。最后一个问题。设置 home_team 的最客观的方式是什么 - game.participants.first.home_team = true,但这并不是非常技术化的,我是否应该在创建比赛表单中设置某些内容? - pshear0
有很多方法可以做你想要的事情,我甚至不知道从哪里开始:P 请查看我的编辑答案。 - Ashitaka
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接