2012-11-29 65 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 
相关问题