Rails 4: counter_cache in has_many :through association with dependent: :destroy

Rails 4: counter_cache in has_many :through association with dependent: :destroy

虽然已经有人问过类似的问题:

none 其中实际上解决了我的问题。

我有三个模型,有一个 has_many :through association :

class User < ActiveRecord::Base
  has_many :administrations
  has_many :calendars, through: :administrations
end

class Calendar < ActiveRecord::Base
  has_many :administrations
  has_many :users, through: :administrations
end

class Administration < ActiveRecord::Base
  belongs_to :user
  belongs_to :calendar
end

加入管理模型具有以下属性:

id
user_id
calendar_id
role

我想数一数每个user有多少个calendars,每个calendar有多少个users

我本来打算 counter_cache 如下:

class Administration < ActiveRecord::Base
  belongs_to :user, counter_cache: :count_of_calendars
  belongs_to :calendar, counter_cache: :count_of_users
end

(当然,相应的迁移将 :count_of_calendars 添加到 users table 并将 :count_of_users 添加到 calendars table.)

但是后来,我偶然发现了 this warning in Rails Guides:

4.1.2.4 :dependent

If you set the :dependent option to:

  • :destroy, when the object is destroyed, destroy will be called on its associated objects.
  • :delete, when the object is destroyed, all its associated objects will be deleted directly from the database without calling their destroy method.

You should not specify this option on a belongs_to association that is connected with a has_many association on the other class. Doing so can lead to orphaned records in your database.

因此,计算每个 user 有多少 calendars 以及每个 calendar 有多少 users 是一个好的做法吗?

嗯,dependent: :destroy会破坏相关记录,但不会更新counter_cache,所以你可能在counter_cache中记错了。相反,您可以实施一个回调来销毁关联的记录,并更新您的 counter_cache

class Calendar < ActiveRecord::Base

  has_many :administrations
  has_many :users, through: :administrations


  before_destroy :delete_dependents

  private
  def delete_dependents
    user_ids = self.user_ids
    User.delete_all(:calendar_id => self.id)
    user_ids.each do |u_id|
      Calendar.reset_counters u_id, :users
    end
  end
end

同样,也为 User 模型实施此方法