我对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
答案 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挂钩中运行,因此播放器以及任何新建的奖励也将被保存。