2013-06-18 71 views
0

我正在嘗試做相當簡單的事情。我有兩個模型,用戶和組。爲簡單起見,讓我們說,他們是這樣的:從HABTM協會刪除記錄

class User < ActiveRecord::Base 
    has_and_belongs_to_many :groups 
end 

class Group < ActiveRecord::Base 
    has_and_belongs_to_many :users 
end 

現在,出於某種原因,我有一個具有同一組兩次的用戶。在Rails控制檯中:

user = User.find(1000) 

=> #<User id: 1000, first_name: "John", last_name: "Doe", active: true, created_at: 
"2013-01-02 16:52:36", updated_at: "2013-06-17 16:21:09"> 

groups = user.groups 

=> [#<Group id: 1, name: "student", is_active: true, created_at: "2012-12-24 15:08:59", 
updated_at: "2012-12-24 15:08:59">, #<Group id: 1, name: "student", is_active: true, 
created_at: "2012-12-24 15:08:59", updated_at: "2012-12-24 15:08:59">] 

user.groups = groups.uniq 

=> [#<Group id: 1, name: "student", is_active: true, created_at: "2012-12-24 15:08:59", 
updated_at: "2012-12-24 15:08:59">] 

user.save 

=> true 

而且有一些SQL輸出已經靜音了。我認爲一切都應該設定,但事實並非如此。這些組沒有更新,並且該用戶仍然具有這兩個。我可以進入連接表並手動刪除重複項,但這似乎很笨拙,粗略和不必要。我在這裏做錯了什麼?

我運行的Rails 3.2.11和Ruby 1.9.3p392

附加說明:我已經試過這許多不同的方式,包括使用user.update_attributes,並使用group_ids而不是組本身,沒有無濟於事。

+0

這是發佈此錯誤,在控制檯中變量名保持一致。 – BSprague

+0

以爲我會先檢查一下容易的東西。 ;-) – pjmorse

+0

向關係船添加'uniq:true'將有助於'獲得'uniq記錄,但它不會停止創建重複記錄。見http://stackoverflow.com/questions/1129781/has-and-belongs-to-many-avoiding-dupes-in-the-join-table –

回答

1

這不起作用的原因是因爲ActiveRecord未處理habtm關聯中的重複項的無效狀態(或針對該問題的任何CollectionAssociation)。不包括在新分配的數組中的任何id被刪除 - 但在這種情況下沒有任何。相關代碼:

# From lib/active_record/associations/collection_association.rb 

def replace_records(new_target, original_target) 
    delete(target - new_target) 
    unless concat(new_target - target) 
    @target = original_target 
    raise RecordNotSaved, "Failed to replace #{reflection.name} because one or more of the " \ 
          "new records could not be saved." 
    end 
    target 
end 

傳遞的'目標'是指定記錄的數組。請注意,對delete(target - new_target)的調用在您的情況下與delete(user.groups - user.groups.uniq)等效,導致傳遞一個空數組(因爲比較基於每個記錄的id屬性)。

相反,你需要再次清除出協會,並重新分配單組:

group = user.groups.first 
user.groups.clear 
user.groups << group 
0

這可能是一種方式來清理這些重複的(它可以處理任何數量的重複關聯的組) :

user = User.find(1000) 

user.groups << user.groups.group_by(&:id).values.find_all {|v| v.size > 1}.each {|duplicates| duplicates.uniq_by! {|obj| obj.id}}.flatten.each {|duplicate| user.groups.delete(duplicate)}