#実行環境
- Ruby 2.2.2
- Rails 4.2.3
- minitest 5.8.4
- fixture
#やりたいこと
親ー子ー孫と関係のあるモデルを一括で削除する
##model
こんな関係を用意します。
親 has_many 子
子 has_many 孫
子 belongs_to 親
孫 belongs_to 子
class Parent < ActiveRecord::Base
has_many :childs
end
class Child < ActiveRecord::Base
belongs_to :parent
has_many :grandchilds
end
class Grandchild < ActiveRecord::Base
belongs_to :child
end
##controller
※ここはとても雑なので参考にしないように。
parent.child.new
のように親レコードから子を作成していることだけ読み取ってください。
class ParentsController < ApplicationController
def create
parent = Parent.new(parent_params)
parent.save
child = parent.childs.new(child_params)
child.save
grandchild = child.grandchilds.new(grandchild_params)
grandchild.save
end
end
#コード
##モデルの改修
parent.rbにdependent: :destroy
を追加し、child.rbにdependent: :delete_all
を追加します。
dependent: :destroy
は親が削除される際に、子を一つずつ削除します。(低速)
dependent: :delete_all
は親が削除される際に子を一括で削除します。(高速)
class Parent < ActiveRecord::Base
has_many :childs, dependent: :destroy
end
class Child < ActiveRecord::Base
belongs_to :parent
has_many :grandchilds, dependent: :delete_all
end
今回の場合、子は孫を持っているので親のhas_many :childs
にdependent: :destroy
を指定しています。
(孫には何も紐付かないので、子のhas_many :grandchilds
にはdependent: :delete_all
でOKです。)
※このあたりはこちらを参考にされると良いかも。→うんたらかんたらRuby 2009-11-26has_manyの:dependentパラメータ
##削除する時
削除する時は普通にこれだけ書けば子も孫も削除されます。
シンプルでいいですね!
class ParentsController < ApplicationController
def destroy
parent = Parent.find(params[:id])
parent.destroy
redirect_to xxxx_path
end
end
#テストコード
fixtureとminitestを使ってテストをします。
##テストデータ
先ほどcontorollerで子を作成する際にparent.childs.new
と書いたのを思い出してください。
同じことをfixtureで書く場合はparent: parent_one
というように書きます。
これを書き忘れるとdependent: :destroy
が効かずこの先のテストで失敗します。
parent_one:
name: Michael
child_one:
parent: parent_one
name: George
grandchild_one:
child: child_one
name: Steve
##テストコード
こんな感じで。
(美しくないけれどminitestでシンプルに書くとこれになってしまう模様。。。)
test "should destroy parent" do
@parent = parents(:parent_one)
assert_difference( 'Parent.count', -1 ) do
assert_difference( 'Child.count', -1 ) do
assert_difference( 'Grandchild.count', -1 ) do
delete :destroy
end
end
end
end