2013-06-18 69 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)}