色々奥が深いと思った(not http://0xcc.net/misc/bad-knowhow.html 笑)
■前提:記事←関連テーブル→ユーザというモデル関係がある。
1
2 class Article< ActiveRecord::Base
3 has_many :articles_users
4 has_many :users, :through => :articles_users
5 end
6
7 class User < ActiveRecord::Base
8 has_many :articles_users, :dependent => :destroy
9 has_many :articles, :through => :articles_users
10 end
11
12
13 class ArticlesUsers < ActiveRecord::Base
14 belongs_to :article
15 belongs_to :user
16 end
■関連テーブルの操作
あるユーザの関連をごそっと別の関連に切り替えたいような場合!
User.articles_users.destroy_allして作り直すという方法もあるが
User.articles_users.replace()という便利なメソッドがある。
AWD第二版(AgileWebDevelopment)で言うとP308。
「○orders.replace(order1,...)
この顧客に関連付けられた注文のセットを、新しいセットに置き換える。現在の子のセットと新しいセットの違いを検出し、それに応じてデータベースの変更を最適化する。」という奴。
1 /ruby/lib/ruby/gems/1.8/gems/activerecord-2.1.0/lib/active_record/associations/association_collection.rb
2
3
4
5 def replace(other_array)
6 other_array.each { |val| raise_on_type_mismatch(val) }
7
8 load_target
9 other = other_array.size < 100 ? other_array : other_array.to_set
10 current = @target.size < 100 ? @target : @target.to_set
11
12 @owner.transaction do
13 delete(@target.select { |v| !other.include?(v) })
14 concat(other_array.select { |v| !current.include?(v) })
15 end
16 end
これは便利。
1.配列を放り込むだけ
「Replace this collection with +other_array+」ということで検索結果、あるいはセッションなどで生成した新しい関連の配列を放り込むだけで使える。
2.以前の関連との差分をチェックして差分だけ反映してくれる。
「This will perform a diff and delete/add only records that have changed.」ということで必要な差分だけチェックして必要なdelete/addをしてくれる。
ちなみにdeleteだと「Railsレシピブック」のP157「Entryオブジェクトobjectとの関連を削除する。Entryオブジェクトobjectの外部キー(blog_id)をNULLにし、関連を削除する。複数の参照元オブジェクトを同時に指定できる。」にある通り、関連テーブルの実レコードは削除されない(外部キーがNULLになるだけ)。関連テーブルにごみが残って気持ち悪い場合は上記モデルのように「:dependent => :destroy」を付けると物理的に削除される。