0

我正在尝试做一些相当简单的事情。我有两个模型,用户和组。为简单起见,假设它们看起来像这样:

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 而不是组本身,但无济于事。

4

2 回答 2

1

这不起作用的原因是 ActiveRecord 没有处理 habtm 关联中重复项的无效状态(或任何与此相关CollectionAssociation的)。id新分配的数组中未包含的任何s 都将被删除 - 但在这种情况下没有。相关代码:

# 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
于 2013-06-18T21:00:31.657 回答
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)}
于 2013-06-18T21:54:34.423 回答