2012-11-29 66 views
0

我有一個has_many:through關聯。球員有很多球隊,球隊有很多球員。加盟模式Affiliation屬於玩家和團隊,並且還具有一個year屬性來跟蹤玩家每年的團隊隸屬關係(或就業)。爲rails 3創建的不正確的數據庫記錄has_many:通過關聯

我似乎無法找出建立基於以下規則具有關聯關係的正確方法:

  1. 創建一個新的球員。
  2. 將可能是新的或現有的團隊關聯起來。所以找到它或創建它,但只有在玩家被保存時才創建它。
  3. 該協會可能會也可能不會包含一年,但只有在保存球員和球隊時才應創建該協會。

玩家模型是這樣的:

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 

我已經成功地創建沒有連接模型屬性的關聯記錄

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

數據庫看起來像:

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 

使用PARAMS像兩隊創建一個新的播放器後:唱的,看起來像PlayersController創建行動

球員

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 

現在,使用PARAMS像兩隊創建一個新的播放器時:

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

數據庫的樣子:

玩家

ID:1,名稱:喬治貝克

ID:2,名稱:比爾·約翰遜

ID:1,城市:布法羅

ID:2,城市:西雅圖

ID:3,城市:底特律

從屬關係

ID:1,player_id:1,TEAM_ID:1,年:零

ID:2,player_id:1,TEAM_ID:2年:零

ID:3,player_id:2 ,TEAM_ID:1,年:空

ID:4,player_id:空,TEAM_ID:3,年:1999

ID:5,player_id:2,TEAM_ID:3,年:空

因此,只有兩個應該創建的三個記錄。聯盟記錄ID:3是正確的。對於id:4,player_id丟失。而對於id:5,這一年不見了。

顯然這是不正確的。我哪裏錯了?

感謝

回答

0

編輯

好吧,我想我有一個更好的解決方案。據我所知,你不能在深度的兩個層面(儘管你可以測試它,也許它的工作原理)使用嵌套的屬性,但沒有什麼能夠阻止我們模擬此行爲:

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 

+0

我試過了,它確實有效。所以謝謝!但我很好奇,有沒有一種方法可以建立關聯,以便「@ group.save」保存所有內容? – glevine

+0

它取決於,我不知道@group是什麼,我只是複製/粘貼它從你的答案 –

+0

對不起,這是一個錯誤。我從另一個例子中複製了代碼,並且在那裏留下了@ @組。我編輯了我的問題,所以'@ group'現在是'@ player'。希望澄清它。 – glevine

0

該解決方案最終爲我工作。但是,如果任何人使用此代碼爲自己的項目,請知道除創建之外,我還沒有測試任何其他操作。我確信,一旦我處理讀取,更新和刪除操作,其中一些內容會發生變化。

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 
相關問題