web-dev-qa-db-ja.com

ネストされたモデルと親の検証

2つのモデルがあります。
-Parenthas_manyChildren;
-Parentaccepts_nested_attributes_forChildren;

class Parent < ActiveRecord::Base
  has_many :children, :dependent => :destroy
  accepts_nested_attributes_for :children, :allow_destroy => true
  validates :children, :presence => true
end

class Child < ActiveRecord::Base
  belongs_to :parent
end

検証を使用してすべての親の子の存在を検証するため、子なしで親を保存することはできません。

parent = Parent.new :name => "Jose"
parent.save
#=> false
parent.children_attributes = [{:name => "Pedro"}, {:name => "Emmy"}]
parent.save
#=> true

検証は機能します。次に、_destroy属性を介して子を破棄します。

parent.children_attributes = {"0" => {:id => 0, :_destroy => true}}
parent.save
#=> true !!!
parent.reload.children
#=> []

ネストされたフォームを介してすべての子を破棄でき、検証に合格します。

実際には、_deleteを介して親から子を削除した後、childrenメソッドは再ロードする前に破棄されたオブジェクトを返すため、検証に合格しました。

parent.children_attributes = {"0" => {:id => 0, :_destroy => true}}
parent.save
#=> true !!!
parent.children
#=> #<Child id:1 ...> # It's actually deleted
parent.reload.children
#=> []

バグですか?

質問は何ですか。問題はそれを修復するための最良の解決策です。私のアプローチは、before_destroyフィルターをChildに追加して、それが最後のものであるかどうかを確認することです。ただし、システムが複雑になります。

40
fl00r

これはおそらくあなたのために働くでしょう、しかし私はそこにはるかに良い答えがあると感じています。それは私にはバグのように聞こえます。

class Parent < ActiveRecord::Base
  validate :must_have_children

  def must_have_children
    if children.empty? || children.all?(&:marked_for_destruction?)
      errors.add(:base, 'Must have at least one child')
    end
  end
end
60
John Douthat

バグではありません。ドキュメントによると

指定された属性が空白ではないことを検証します(Object#blank?で定義)。

validates :children, :presence => trueはまったく同じです。ドキュメントには、アソシエーションで使用しようとするとどうなるかは記載されていません。 validateを使用したカスタム検証を使用する必要があります。

validates_presence_of関連付けでhas_manyを使用すると、クラスアレイのオブジェクトである関連付けchildrenblank?が呼び出されます。 blank?Arrayに対して定義されていないため、Rails内でキャッチされたmethod_missingを起動します。通常、それはあなたが望むことをしますが、私はそれがRails 3.1rcとRuby 1.8.7で本当にひどい方法で失敗することを発見しました:それは静かにの変更を元に戻します何が起こっているのかを知るのに数時間かかりました。

0
Tomáš K.