2014-09-24 102 views
2

我是Ruby和Rails的新手,請耐心等待。如何防止連接表中的重複記錄

我有兩個模型播放器和獎勵通過has_many通過如下關係加入。我的玩家模型有一個屬性點。當玩家獲得積分時,他們可以獲得獎勵。我想要做的就是在更新前運行的Player模型中添加一個方法,併爲他們下面的點提供適當的獎勵。

但是我想這樣做,如果玩家已經有獎勵它不會被複制,也不會導致錯誤。

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

嘗試'validates_uniqueness_of:reward_id,範圍:: player_id',(':reward_id:player_id')對必須是uniq – 2014-09-24 03:31:59

+0

@PavittarGill使用您的方法會導致運行時出現語法錯誤。 – Minutiae 2014-09-24 13:27:18

+0

'validates_uniqueness_of:reward_id,scope::player_id' – 2014-09-26 03:23:26

回答

1

你應該驗證它在DB也

加入遷移文件 -

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

編輯follwing: 我認識到我以前的答案是行不通的,因爲新獎勵與父級玩家模型無關。

爲了正確地關聯這兩者,您需要使用build。 見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 

當你建立一個協會,它把它添加到父,這樣當父保存,孩子也得救了。例如。

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鉤運行,玩家將被保存以及任何新建獎勵爲好。

+0

我正在嘗試你的方法,但它不工作。它總是創建一個新的獎勵,而不是找到已經存在的獎勵,並創建一個將其與玩家記錄相關聯的EarnedReward記錄。 – Minutiae 2014-09-24 12:41:13

+0

我已經添加了一個更清晰的例子。 我也調整了什麼時候的條件,以便他們不需要正好1000點才能獲得獎勵。 – 2014-09-24 23:30:06

+0

我已更新我的帖子以包含您鏈接到的示例。你能看看並告訴我,如果我做得對嗎?我對Rails和Ruby相當陌生,你認爲你可以解釋發生了什麼? – Minutiae 2014-09-25 20:46:36