简体   繁体   English

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

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

I have a has_many :through association. 我有一个has_many:through关联。 Players have many Teams and Teams have many Players. 玩家有很多团队,而团队有很多玩家。 The join model, Affiliation, belongs to Players and Teams, and also has a year attribute to keep track of a player's team affiliation (or employment) from year to year. 加入模型Affiliation属于“玩家和团队”,并且还具有“ year属性来跟踪玩家year的团队隶属关系(或雇用情况)。

I can't seem to figure out the right way to build an association based on the following rules: 我似乎无法找出基于以下规则建立关联的正确方法:

  1. Create a new player. 创建一个新的播放器。
  2. Associate a team that may be new or existing. 关联一个新的或现有的团队。 So find it or create it, but only create it if the player is saved. 因此找到它或创建它,但是只有在保存播放器后才创建它。
  3. The association may or may not include a year, but the association should only be created if the player and team are saved. 关联可以包括也可以不包括年份,但是只有在保存了球员和球队的情况下才可以创建关联。

The Player model looks like: 播放器模型如下所示:

class Player < ActiveRecord::Base
  attr_accessible :name

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

The Team model looks like: 团队模型如下所示:

class Team < ActiveRecord::Base
  attr_accessible :city

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

The Affiliation model looks like: 隶属关系模型如下所示:

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

I have been successful at creating the association records without the join model attribute using a create action in the PlayersController that looks like: 我已经成功使用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

After creating a new player with two teams using params like: 在使用如下参数创建具有两支球队的新球员之后:

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

the database looks like: 该数据库如下所示:

players 玩家们

id: 1, name: George Baker id:1,名字:乔治·贝克

teams 团队

id: 1, city: Buffalo id:1,城市:布法罗

id: 2, city: Seattle id:2,城市:西雅图

affiliations 隶属关系

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

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

When I try to introduce the year, things fall apart. 当我尝试介绍年份时,事情就崩溃了。 My most recent attempt at the create action in the PlayersController looks like: 我最近在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

Now, when creating a new player with two teams using params like: 现在,当使用以下参数创建由两支球队组成的新球员时:

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

the database looks like: 该数据库如下所示:

players 玩家们

id: 1, name: George Baker id:1,名字:乔治·贝克

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

teams 团队

id: 1, city: Buffalo id:1,城市:布法罗

id: 2, city: Seattle id:2,城市:西雅图

id: 3, city: Detroit id:3,城市:底特律

affiliations 隶属关系

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

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

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

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

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

So three records were created when only two should have been. 因此,当只有两个记录时,将创建三个记录。 The affiliation record id: 3 is correct. 关联记录ID:3是正确的。 For id: 4, the player_id is missing. 对于ID:4,缺少player_id。 And for id: 5, the year is missing. 而对于ID:5,则缺少年份。

Obviously this is incorrect. 显然这是不正确的。 Where am I going wrong? 我要去哪里错了?

Thanks 谢谢

Edit 编辑

Ok, i think i have a better solution. 好的,我想我有一个更好的解决方案。 AFAIK, you can't use nested attributes on two levels of depth (though you could test it, maybe it works), but nothing prevents us to simulate this behavior : 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

Now, doing this : 现在,这样做:

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

should create all the required records, and still rollback everything in case of problems (because the save itself is already wrapped in a transaction). 应该创建所有必需的记录,并在出现问题时仍回滚所有内容(因为save本身已经包装在事务中)。 As a bonus, all the errors will be associated to @player ! 另外,所有错误都将与@player关联!

How about this ? 这个怎么样 ?

 
 
 
  
  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
 
  

This solution ended up working for me. 该解决方案最终为我工作。 If anyone uses this code for their own project, however, please know that I haven't tested any other actions besides create. 但是,如果有人在自己的项目中使用此代码,请注意,除了create之外,我没有测试过其他任何操作。 I'm certain some of this will change once I deal with read, update and delete. 我敢肯定,一旦我处理了读取,更新和删除操作,其中某些操作将会改变。

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.

相关问题 Rails-has_many:通过查找没有关联的记录 - Rails - has_many :through find records which have no association 如何将记录添加到has_many:通过rails中的关联 - how to add records to has_many :through association in rails Rails 4:通过has_many关联创建新记录 - Rails 4: Create new records through has_many association Rails:通过关联显示has_many上的所有关联记录 - Rails: Show all associated records on a has_many through association 如何将记录添加到has_many:通过Ruby on Rails中的关联 - how to add records to has_many :through association in Ruby on rails 将关联记录的顺序保存在Rails has_many中:通过关联 - Saving the order of associated records in a Rails has_many :through association 如何通过Rails 3.2中的关联在has_many中创建记录 - How to create records in has_many through association in rails 3.2 Rails 4通过关联和数据库嵌套表格has_many - Rails 4 Nested form has_many through association and database 显示has_many,:通过关联记录 - Displaying has_many, :through association records 多态关联和has_many:在尝试获取直通关联记录时,通过生成错误的sql,rails? - Polymorhic association, and has_many :through generating wrong sql when trying to get the through association records, rails?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM