我正在尝试做一些相当简单的事情。我有两个模型,用户和组。为简单起见,让我们说它们看起来像这样:
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 :(得分: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
属性)。
相反,您需要清除关联,然后再次重新分配单个组:
{{1}}
答案 1 :(得分: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)}