钢轨在3的has_many不正确的数据库记录:通过协会 [英] incorrect database records created for rails 3 has_many :through association

查看:147
本文介绍了钢轨在3的has_many不正确的数据库记录:通过协会的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个的has_many:通过关联。玩家有很多球队和球队有很多球员。联接模型,隶属关系,属于球员和球队,并且还具有属性来跟踪一名球员的球队的关系(或就业)每年的。

I have a has_many :through association. 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.

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

I can't seem to figure out the right way to build an association based on the following rules:


  1. 创建一个新的球员。

  2. 关联一个团队,可能是新的或现有的。所以找到它或创建它,但只有创建它,如果球员被保存。

  3. 的关联可以或可以不包括一年,但是,如果播放器和团队都保存在关联应该只被创建。

玩家模型如下:

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

我已经成功地使用看起来像PlayersController创建操作,而不加入模型属性创建关联记录:

I have been successful at creating the association records without the join model attribute using a create action in the PlayersController that looks like:

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就像两支球队一个新的球员之后:

After creating a new player with two teams using params like:

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

数据库如下:

玩家

ID:1,名称:乔治贝克

id: 1, name: George Baker

小组

ID:1,城市:布法罗

id: 1, city: Buffalo

ID:2,城市:西雅图

id: 2, city: Seattle

隶属关系

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

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

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

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

当我尝试引进这一年,事情土崩瓦解。我最近在该PlayersController创建操作的尝试是这样的:

When I try to introduce the year, things fall apart. My most recent attempt at the create action in the PlayersController looks like:

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两队创建一个新的播放器时:

Now, when creating a new player with two teams using params like:

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

数据库如下:

玩家

ID:1,名称:乔治贝克

id: 1, name: George Baker

ID:2,名称:比尔·约翰逊

id: 2, name: Bill Johnson

小组

ID:1,城市:布法罗

id: 1, city: Buffalo

ID:2,城市:西雅图

id: 2, city: Seattle

ID:3,城市:底特律

id: 3, city: Detroit

隶属关系

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

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

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

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

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

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

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

id: 4, player_id: null, team_id: 3, year: 1999

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

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

当只有两个应该已经建立,因此三个记录。隶属关系记录ID:3是正确的。对于ID:4,player_id缺失。而对于ID:5,今年是缺少

So three records were created when only two should have been. The affiliation record id: 3 is correct. For id: 4, the player_id is missing. And for id: 5, the year is missing.

这显然是不正确。我在哪里去了?

Obviously this is incorrect. Where am I going wrong?

感谢

推荐答案

修改

好吧,我想我有一个更好的解决方案。据我所知,你不能在深度的两个层面(尽管你可以测试它,也许它的工作原理)使用嵌套的属性,但没有prevents我们的模拟的这种行为:

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 :

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

应该创建所有必需的记录,并在出现问题时仍然回滚一切(因为保存本身已经是包裹在一个事务)。作为奖励,所有的错误将关联到 @player

should create all the required records, and still rollback everything in case of problems (because the save itself is already wrapped in a transaction). As a bonus, all the errors will be associated to @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

这篇关于钢轨在3的has_many不正确的数据库记录:通过协会的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆