0

我有一个 has_many :through 关联。球员有很多球队,球队有很多球员。加入模型 Affiliation 属于 Players 和 Teams,并且还具有每年year跟踪玩家的团队隶属关系(或就业)的属性。

我似乎无法根据以下规则找出建立关联的正确方法:

  1. 创建一个新玩家。
  2. 关联一个可能是新的或现有的团队。所以找到它或创建它,但只有在保存玩家时才创建它。
  3. 关联可能包括也可能不包括一年,但只有在保存球员和球队的情况下才能创建关联。

Player 模型如下所示:

class Player < ActiveRecord::Base
  attr_accessible :name
  
  has_many :affiliations, :dependent => :destroy
  has_many :teams, :through => :affiliations
end

团队模型如下所示:

class Team < ActiveRecord::Base
  attr_accessible :city
  
  has_many :affiliations, :dependent => :destroy
  has_many :players, :through => :affiliations
end

隶属关系模型如下所示:

class Affiliation < ActiveRecord::Base
  attr_accessible :player_id, :team_id, :year
  belongs_to :player
  belongs_to :team
end

我已经成功地使用 PlayersController 中的创建操作创建了没有连接模型属性的关联记录,如下所示:

class PlayersController < ApplicationController
  def create
    @player = Player.new(params[:player].except(:teams))
    
    unless params[:player][:teams].blank?
      params[:player][:teams].each do |team|
        team_to_associate = Team.find_or_initialize_by_id(team[:id], team.except(:year)
        @player.teams << team_to_associate
      end
    end
    
    @player.save
    respond_with @player
  end
end

使用以下参数创建具有两支球队的新球员后:

{"player"=>{"name"=>"George Baker", "teams"=>[{"city"=>"Buffalo"}, {"city"=>"Detroit"}]}}

数据库看起来像:

球员

id:1,姓名:George Baker

团队

id:1,城市:布法罗

id:2,城市:西雅图

隶属关系

id: 1, player_id: 1, team_id: 1, year: null

id: 2, player_id: 1, team_id: 2, year: null

当我试图介绍这一年时,事情就崩溃了。我最近在 PlayersController 中创建操作的尝试如下所示:

class PlayersController < ApplicationController
  def create
    @player = Player.new(params[:player].except(:teams))
    
    unless params[:player][:teams].blank?
      params[:player][:teams].each do |team|
        team_to_associate = Team.find_or_initialize_by_id(team[:id], team.except(:year)
        // only additional line...
        team_to_associate.affiliations.build({:year => team[:year]})
        @player.teams << team_to_associate
      end
    end
    
    @player.save
    respond_with @player
  end
end

现在,当使用以下参数创建具有两支球队的新球员时:

{"player"=>{"name"=>"Bill Johnson", "teams"=>[{"id"=>"1"}, {"city"=>"Detroit", "year"=>"1999"}]}}

数据库看起来像:

球员

id:1,姓名:George Baker

id:2,姓名:Bill Johnson

团队

id:1,城市:布法罗

id:2,城市:西雅图

id:3,城市:底特律

隶属关系

id: 1, player_id: 1, team_id: 1, year: null

id: 2, player_id: 1, team_id: 2, year: null

id: 3, player_id: 2, team_id: 1, year: null

id: 4, player_id: null, team_id: 3, 年份: 1999

id: 5, player_id: 2, team_id: 3, year: null

因此,在应该只创建两个的情况下创建了三个记录。隶属关系记录 id: 3 是正确的。对于 id: 4,缺少 player_id。对于 id: 5,缺少年份。

显然这是不正确的。我哪里错了?

谢谢

4

2 回答 2

0

编辑

好的,我想我有更好的解决方案。AFAIK,你不能在两个深度级别上使用嵌套属性(虽然你可以测试它,也许它有效),但没有什么能阻止我们模拟这种行为:

class Player < ActiveRecord::Base
  has_many :affiliations
  has_many :teams, through: :affiliations
  accespts_nested_attributes_for :affiliations, allow_destroy: true
end

class Affiliation < ActiveRecord::Base
  belongs_to :player
  belongs_to :team

  validates :player, presence: true
  validates :team,   presence: true

  attr_accessor :team_attributes 

  before_validation :link_team_for_nested_assignment

  def link_team_for_nested_assignment
    return true unless team.blank?
    self.team = Team.find_or_create_by_id( team_attributes )
  end

现在,这样做:

@player = Player.new( 
            name: 'Bill Johnson', 
            affiliations_attributes: [
              {year: 1999, team_attributes: {id: 1, city: 'Detroit}},
              {team_attributes: {city: 'Somewhere else'}}
            ]
          )
@player.save

应该创建所有必需的记录,并且在出现问题时仍然回滚所有内容(因为它save本身已经包含在事务中)。作为奖励,所有错误都将与@player

这个怎么样 ?

class PlayersController < ApplicationController
  def create

    ActiveRecord::Base.transaction do

      @player = Player.new(params[:player].except(:teams))
      raise ActiveRecord::Rollback unless @player.save # first check

      unless params[:player][:teams].blank?
        @teams = []
        params[:player][:teams].each do |team|

          team_to_associate = Team.find_or_initialize_by_id(team[:id], team.except(:year))
          raise ActiveRecord::Rollback unless team_to_associate.save # second check

          if team[:year]
            affiliation = team_to_associate.affiliations.build(player: @player, year: team[:year])
            raise ActiveRecord::Rollback unless affiliation.save # third check
          end
          @teams << team_to_associate # keep the object so we have access to errors
        end
      end
    end


      flash[:notice] = "ok"
  rescue ActiveRecord::Rollback => e
    flash[:alert]  = "nope"
  ensure
    respond_with @group
  end
end

于 2012-11-29T12:59:27.283 回答
0

这个解决方案最终对我有用。但是,如果有人将此代码用于他们自己的项目,请知道我没有测试除 create 之外的任何其他操作。我敢肯定,一旦我处理读取、更新和删除,其中一些会改变。

class Player < ActiveRecord::Base
  attr_accessible :name

  has_many :affiliations, :dependent => :destroy
  has_many :teams, :through => :affiliations
  accepts_nested_attributes_for :affiliations, :allow_destroy => true
  attr_accessible :affiliations_attributes
end

class Team < ActiveRecord::Base
  attr_accessible :city

  has_many :affiliations, :dependent => :destroy
  has_many :players, :through => :affiliations
end

class Affiliation < ActiveRecord::Base
  attr_accessible :player_id, :team_id, :team_attributes, :year
  belongs_to :player
  belongs_to :team
  accepts_nested_attributes_for :team

  def team_attributes=(team_attributes)
    self.team = Team.find_by_id(team_attributes[:id])
    self.team = Team.new(team_attributes.except(:id)) if self.team.blank?
  end
end

class PlayersController < ApplicationController
  def create
    player_params = params[:player].except(:teams)
    affiliation_params = []

    unless params[:player][:teams].blank?
      params[:player][:teams].each do |team|
        affiliation = {}
        affiliation[:year] = team[:year] unless team[:year].blank?
        affiliation[:team_attributes] = team.except(:year)
        affiliation_params << affiliation
      end
    end

    player_params[:affiliation_attributes] = affiliation_params unless affiliation_params.blank?

    @player = Player.new(player_params)
    @player.save

    respond_with @player
  end
end
于 2012-12-02T05:40:22.257 回答