如何使用has_and_belongs_to_many将新模型与现有模型相关联

aar*_*ell 6 ruby many-to-many model ruby-on-rails associations

我有两个模型,有很多关系使用has_and_belongs_to_many.像这样:

class Competition < ActiveRecord::Base
  has_and_belongs_to_many :teams
  accepts_nested_attributes_for :teams
end

class Team < ActiveRecord::Base
  has_and_belongs_to_many :competitions
  accepts_nested_attributes_for :competitions
end
Run Code Online (Sandbox Code Playgroud)

如果我们假设我已经在数据库中创建了几个比赛,那么当我创建一个新的团队时,我想使用嵌套表单将新团队与任何相关的比赛相关联.

在这一点上,我确实需要帮助(已经坚持了好几个小时!)我认为我现有的代码已经以错误的方式解决了这个问题,但我会在以下情况下展示:

class TeamsController < ApplicationController
  def new
    @team = Team.new
    @competitions.all
    @competitions.size.times {@team.competitions.build}
  end
  def create
    @team = Team.new params[:team]
    if @team.save
      # .. usual if logic on save
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

并且观点......这是我真正陷入困境的地方所以我不会同时发布我的努力.我想要的是每个比赛的复选框列表,以便用户可以选择适当的比赛,并且不选择那些比赛.

我真的坚持这个,所以欣赏任何指向正确的方向,你可以提供:)

tad*_*man 4

将模型连接在一起的 has_and_belongs_to_many 方法已被弃用,取而代之的是新的 has_many ... :through 方法。管理以 has_and_belongs_to_many 关系存储的数据非常困难,因为 Rails 没有提供默认方法,但 :through 方法是一流的模型,可以这样进行操作。

由于它与您的问题有关,您可能想像这样解决它:

class Competition < ActiveRecord::Base
  has_many :participating_teams
  has_many :teams,
    :through => :participating_teams,
    :source => :team
end

class Team < ActiveRecord::Base
  has_many :participating_teams
  has_many :competitions,
    :through => :participating_teams,
    :source => :competition
end

class ParticipatingTeam < ActiveRecord::Base
  belongs_to :competition
  belongs_to :team
end
Run Code Online (Sandbox Code Playgroud)

在创建团队本身时,您应该构建表单,以便将收到的参数之一作为数组发送。通常,这是通过将所有复选框字段指定为相同名称来完成的,例如“competitions[]”,然后将每个复选框的值设置为比赛的 ID。然后控制器看起来像这样:

class TeamsController < ApplicationController
  before_filter :build_team, :only => [ :new, :create ]

  def new
    @competitions = Competitions.all
  end

  def create
    @team.save!

    # .. usual if logic on save
  rescue ActiveRecord::RecordInvalid
    new
    render(:action => 'new')
  end

protected
  def build_team
    # Set default empty hash if this is a new call, or a create call
    # with missing params.
    params[:team] ||= { }

    # NOTE: HashWithIndifferentAccess requires keys to be deleted by String
    # name not Symbol.
    competition_ids = params[:team].delete('competitions')

    @team = Team.new(params[:team])

    @team.competitions = Competition.find_all_by_id(competition_ids)
  end
end
Run Code Online (Sandbox Code Playgroud)

设置复选框列表中每个元素的选中或未选中状态是通过以下方式完成的:

checked = @team.competitions.include?(competition)
Run Code Online (Sandbox Code Playgroud)

其中“竞争”是被反复提及的。

您可以轻松地在竞赛列表中添加和删除项目,或者简单地重新分配整个列表,Rails 将根据它找出新的关系。您的更新方法看起来与新方法没有什么不同,只是您使用的是 update_attributes 而不是 new。

  • 感谢您的答复(对于花了几天时间才回复表示歉意)。您的解决方案效果很好,尽管我花了一些时间才弄清楚如何构建表单。为了其他人偶然发现这一点的好处,虽然团队表单是使用 form_for 帮助程序生成的,但对于比赛部分,我手动创建了复选框,如下所示: &lt;%= check_box_tag "team[competitions][]", racing.id, @ team.competitions.include?(competition), :id =&gt; "team_competitions_#{competition.id}" %&gt; (2认同)