如何防止连接表中的重复记录

时间:2014-09-24 01:39:06

标签: ruby-on-rails ruby-on-rails-4

我对Ruby和Rails很陌生,所以请耐心等待。

我有两个模型玩家,奖励通过has_many通过关系加入,如下所示。我的播放器模型有一个属性点。当玩家获得积分时,他们会获得奖励。我想要做的是在播放器模型上放置一个方法,该方法将在更新之前运行,并为下面的点提供适当的奖励。

但是我想这样做,如果玩家已经获得了奖励,那么它就不会被复制,也不会导致错误。

class Player < ActiveRecord::Base
  has_many :earned_rewards, -> { extending FirstOrBuild }
  has_many :rewards, :through => :earned_rewards

  before_update :assign_rewards, :if => :points_changed?

  def assign_rewards
    case self.points
    when 1000
      self.rewards << Reward.find_by(:name => "Bronze")
    when 2000
      self.rewards << Reward.find_by(:name => "Silver")
    end
end

class Reward < ActiveRecord::Base
  has_many :earned_rewards
  has_many :players, :through => :earned_rewards
end

class EarnedReward < ActiveRecord::Base
  belongs_to :player
  belongs_to :reward

  validates_uniqueness_of :reward_id, :scope => [:reward_id, :player_id]
end

module FirstOrBuild
  def first_or_build(attributes = nil, options = {}, &block)
    first || scoping{ proxy_association.build(attributes, &block) }
  end
end

2 个答案:

答案 0 :(得分:2)

你应该在db中验证它

在迁移文件中添加以下内容 -

add_index :earnedrewards, [:reward_id, :player_id], unique: true

答案 1 :(得分:2)

编辑: 我已经意识到我之前的答案不起作用,因为新的奖励与父级玩家模型无关。

为了正确关联这两者,您需要使用构建。 见https://stackoverflow.com/a/18724458/4073431

简而言之,我们只想构建它,如果它还不存在,那么我们先调用||建立

具体做法是:

class Player < ActiveRecord::Base
  has_many :earned_rewards
  has_many :rewards, -> { extending FirstOrBuild }, :through => :earned_rewards

  before_update :assign_rewards, :if => :points_changed?

  def assign_rewards
    case self.points
    when 1000...2000
      self.rewards.where(:name => "Bronze").first_or_build
    when 2000...3000
      self.rewards.where(:name => "Silver").first_or_build
    end
end

class Reward < ActiveRecord::Base
  has_many :earned_rewards
  has_many :players, :through => :earned_rewards
end

class EarnedReward < ActiveRecord::Base
  belongs_to :player
  belongs_to :reward

  validates_uniqueness_of :reward_id, :scope => [:reward_id, :player_id]
end

module FirstOrBuild
  def first_or_build(attributes = nil, options = {}, &block)
    first || scoping{ proxy_association.build(attributes, &block) }
  end
end

构建关联时,会将其添加到父级,以便在保存父级时也会保存子级。 E.g。

pry(main)> company.customers.where(:fname => "Bob")
  Customer Load (0.1ms)  SELECT "customers".* FROM "customers"
=> [] # No customer named Bob
pry(main)> company.customers.where(:fname => "Bob").first_or_build
=> #<Customer id: nil, fname: "Bob"> # returns you an unsaved Customer
pry(main)> company.save
=> true
pry(main)> company.reload.customers
=> [#<Customer id: 1035, fname: "Bob">] # Bob gets created when the company gets saved
pry(main)> company.customers.where(:fname => "Bob").first_or_build
=> #<Customer id: 1035, fname: "Bob"> # Calling first_or_build again will return the first Customer with name Bob

由于我们的代码在before_update挂钩中运行,因此播放器以及任何新建的奖励也将被保存。