acts_as_paranoidとhas_many :throughでちょいはまる

データの削除を削除キーでしてくれるプラグインacts_as_paranoidですが、
has_many :throughを同時に使ってちょっとはまったんでメモ。


groupsテーブルとmembersテーブルが中間テーブルAssociationsで
has_many :throughを用いて、多対多の関係にあるとき、RailsのModelでは

#Grouop model
class Group < ActiveRecord::Base
  has_many :associations
  has_many :members, :through => :associations
end

#Member model
class Member < ActiveRecord::Base
  has_many :associations
  has_many :groups, :through => :associations
end

#Association model
class Association < ActiveRecord::Base
  belongs_to :group
  belongs_to :member
end

という感じになりますが、
このとき中間テーブルにacts_as_paranoidを

#Association model
class Association < ActiveRecord::Base
  belongs_to :group
  belongs_to :member

  acts_as_paranoid
end

って感じに設定してるとき、GroupとMemberの関連をなくすために
Associationの関連部分をdestroyしても、関連がなくなりません。

それは、Associationをfindしたら、削除されてるように見えますが、
実際は、データとして残ってる(acts_as_paranoidのおかげ)ので、


もしid=1のグループのユーザとの関連を削除していたとしても、

Group.find(1).users

は削除前の値を返します。


これを改善するためには、以下のようにGroupとMemberモデルを設定します。

#Grouop model
class Group < ActiveRecord::Base
  has_many :associations
  # 変更箇所
  has_many :members, :through => :associations, :conditions => "associations.deleted_at IS NULL"
end

#Member model
class Member < ActiveRecord::Base
  has_many :associations
  # 変更箇所
  has_many :groups, :through => :associations, :conditions => "associations.deleted_at IS NULL"
end

#Association model
class Association < ActiveRecord::Base
  belongs_to :group
  belongs_to :member

  acts_as_paranoid
end

これで、削除したやつは除いた値が得られます。
まぁ明示的に、acts_as_paranoidがやってる処理を書いてるだけですけど。