Rails3での子の削除とvalidation

Rails3でone-to-manyな関係にある子も同時に更新する際にはaccepts_nested_attributes_forを使う。削除も許すにはオプションでallow_destroy: trueを渡して、送るパラメータに_destroyを追加すれば削除できる。

accepts_nested_attributes_forによれば

All changes to models, including the destruction of those marked for destruction, are saved and destroyed automatically and atomically when the parent model is saved.

と削除されるのは親がsaveされたタイミングなので気をつけないとvalidationをすり抜けてしまうことがある。削除されるかどうかはmarked_for_destruction?で判断できる。

class Parent < ActiveRecord::Base
  has_many :children
  accepts_nested_attributes_for :children, allow_destroy: true
  validate do
    # children.sizeにすると更新の際に3未満にできてしまう
    errors.add :children, "の数は3以上にしてください。" if actual_children_size < 3
  end

private
  def actual_children_size
    children.reject(&:marked_for_destruction?).size
  end
end