web-dev-qa-db-ja.com

accepts_nested_attributes_for子関連付けの検証に失敗しました

Railsモデルの1つでaccepts_nested_attributes_forを使用していて、親を作成した後で子を保存したい。

フォームは完全に機能しますが、検証は失敗します。簡単にするために、次のことを想像してください。

class Project < ActiveRecord::Base
  has_many :tasks
  accepts_nested_attributes_for :tasks
end

class Task < ActiveRecord::Base
  belongs_to :project

  validates_presence_of :project_id
  validates_associated :project
end

そして私は走っています:

Project.create!(
  :name => 'Something',
  :task_attributes => [ { :name => '123' }, { :name => '456' } ]
)

プロジェクトモデルを保存すると、プロジェクトが保存されていないため、project_idがないため、タスクの検証が失敗します。

Railsは以下のパターンに従っているようです:

  • プロジェクトを検証
  • タスクの検証
  • プロジェクトを保存
  • タスクを保存

パターンは次のとおりです。

  • プロジェクトを検証
  • 合格時:プロジェクトを保存して続行...
  • タスクの検証
    • 合格時:タスクの保存
    • 失敗した場合:プロジェクトを削除します(おそらくロールバックしますか?)

だから私の質問は次のように要約されます:親(プロジェクト)が保存された後、Railsを実行してproject_id =(またはproject =)メソッドを実行し、子(タスク)で検証する方法は?ただし、子(タスク)が無効な場合、親(プロジェクト)モデルは保存しませんか?

何か案は?

71
Ryan Townsend

Rails 2の場合はこの回答を使用します。それ以外の場合は、:inverse_of回答

notで関連するプロジェクトが有効かどうかproject_idを確認することで、これを回避できます。


class Task < ActiveRecord::Base
  belongs_to :project

  validates_presence_of :project_id, :unless => lambda {|task| task.project.try(:valid?)}
  validates_associated :project
end
12
The Who

使用する :inverse_ofおよびvalidates_presence_of :parent。これにより、検証の問題が修正されます。

   class Dungeon < ActiveRecord::Base
     has_many :traps, :inverse_of => :dungeon
   end

   class Trap < ActiveRecord::Base
     belongs_to :dungeon, :inverse_of => :traps
     validates_presence_of :dungeon
   end

http://apidock.com/Rails/ActiveModel/Validations/HelperMethods/validates_presence_of

https://github.com/Rails/rails/blob/73f2d37505025a446bb5314a090f412d0fceb8ca/activerecord/test/cases/nested_attributes_test.rb

161
boblin

IDではなく、関係のみを検証します。

class Task < ActiveRecord::Base
  belongs_to :project

  validates_presence_of :project
end

関連付けが設定されるとすぐに、ActiveRecordはモデルが保存されているかどうかにかかわらず、検証が成功したと見なします。タスクのプロジェクトが常に保存されるように、自動保存も調査することをお勧めします。

class Task < ActiveRecord::Base
  belongs_to :project, :autosave => true

  validates_presence_of :project
end

残念ながら、上記の提案はどれもRails 2.3.5。

私の場合、ネストされた属性を使用して両方が作成されている場合、タスク内のプロジェクトは常にnilです。 validates_presence_ofを削除した場合のみ、作成は正常に行われます。単体テストとログは、すべてが正しく作成されたことを示しています。

したがって、Railsの代わりにDBに制約を追加する傾向があります。そもそもそれがより信頼性が高いようです。

2
Michael Reinsch

プロジェクトを作成し、検証に合格した場合にのみプロジェクトを追加できます。

tasks = params.delete(:task_attributes)
if Project.create(params)
  Project.update_attributes(:task_attributes => tasks)
end

チャオ

1
user119264

Bigoの提案とは逆に、最初に親オブジェクトを保存してから子を保存することは常に許容できるわけではありません。通常、オブジェクトの保存を開始する前に、すべてのオブジェクトを検証する必要があります。これにより、ユーザーは入力フォームを再編集してエラーを修正できます。

あなたが説明する問題はRails 3.0で修正されます。Lighthouseチケットへのリンクを投稿したはずですが、私は新しいユーザー(#fail)なので、stackoverflow.comではこれを許可していません。 。しかし、当面は、「バグ」を修正するプラグイン「 parental_control 」を使用できます。

0
Thomas Watson