odeの開発メモ日記

プログラマーやってます。今までの道のり ポケコンbasic→C(DirectX5 ネットやろうぜ)→Perl(ちょろっと)→Java→C#→Ruby→Android(Java)

acts_as_paranoidで関連を扱う場合

acts_as_paranoidとはモデルを論理削除できるようにするrailsプラグインです。


簡単な使用では問題ないのですが
関連を使った場合には消したデータを見てしまう可能性がありました。

やりたいこと

例として学校クラスと生徒クラスがあったとして
学校を削除できたとする。(過疎化、少子化のせいですかねぇー)


この場合に生徒一覧を取得する、但し削除した学校の生徒は出さない
といったことをする

クラス

class School < ActiveRecord::Base
  acts_as_paranoid
  has_many :students
end

class Student < ActiveRecord::Base
  belongs_to :school
end

方法と結果

1番
School.students.find(:all)
OK.問題ない


2番
Student.find(:all, :include=>:school, :condition=>"school.created_at > '2001/1/1'")
NG.削除した学校の生徒も取得してしまう


3番
Student.find(:all, :include=>:school)
OK.これは大丈夫

説明

このプラグインはacts_as_paranoidと定義してあるモデルからfindした場合にしか
発動しないらしい。(なんちゃってソース見で)
そのため2番目のがNGになる。
(Studentにはacts_as_paranoidを定義していないせい)


では、3番目のがOKになるのは何故か?
それは恐らくrails2.1だからだろう。
rails2.1から単純な結合はjoinを使った1回のsqlではなく
各モデルに対する2回のsqlに分けるようになったためだ。
動作としてStudentを取得するsqlを発行後に、Schoolを取得するsqlを発行する。
ということでShoolに対してfindしてるのでacts_as_paranoidの対象となる。


じゃあ、何故2番目の例がNGになるかというと
conditionに結合先の条件が入っているためだ。この場合はrails2.0以前のものと動作が同じになり
joinする1回のsqlになる。

2番目の場合の回避策としてはconditionに明示的にdeleted_at is nullとのチェック処理を入れることで
対応できる。(まぁ、あたりまえですねぇ。。自動で関連先も見る機能がほしいけどやっぱ複雑なんだろうなぁ。。)

実行環境

rails2.1.1