繁体   English   中英

为rails 3 has_many创建的错误数据库记录:通过关联

[英]incorrect database records created for rails 3 has_many :through association

我有一个has_many:through关联。 玩家有很多团队,而团队有很多玩家。 加入模型Affiliation属于“玩家和团队”,并且还具有“ year属性来跟踪玩家year的团队隶属关系(或雇用情况)。

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

  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

我已经成功使用PlayersController中的create动作创建了没有join model属性的关联记录,如下所示:

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,名字:乔治·贝克

团队

id:1,城市:布法罗

id:2,城市:西雅图

隶属关系

id:1,player_id:1,team_id:1,年份:null

id:2,player_id:1,team_id:2,年份: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,名字:乔治·贝克

id:2,名字:比尔·约翰逊

团队

id:1,城市:布法罗

id:2,城市:西雅图

id:3,城市:底特律

隶属关系

id:1,player_id:1,team_id:1,年份:null

id:2,player_id:1,team_id:2,年份:null

id:3,player_id:2,team_id:1,年份:null

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

id:5,player_id:2,team_id:3,年份​​:null

因此,当只有两个记录时,将创建三个记录。 关联记录ID:3是正确的。 对于ID:4,缺少player_id。 而对于ID:5,则缺少年份。

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

谢谢

编辑

好的,我想我有一个更好的解决方案。 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
 
  

该解决方案最终为我工作。 但是,如果有人在自己的项目中使用此代码,请注意,除了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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM