0

ネストされた子と孫のレコードを作成しようとしています。子は親と孫の両方に属します。まだ保存されていないため、子は孫の存在を検証しません。

私は Rails 2.3.11、Formtastic、InheritedResources、および Haml を使用していますが、その他はすべて正しく動作しているようです。たとえば、孫の検証エラーが親フォームに適切に入力され、無効な値が記憶されてユーザーに表示されます。 . 親モデルは、すべてが有効でない限り、更新しようとさえしません。

私のコードは次のようなものですが、問題のドメインは異なります。

class Project < ActiveRecord::Base
  has_many :meetings, :dependent => :destroy
  accepts_nested_attributes_for :meetings
end

class Meeting < ActiveRecord::Base
  belongs_to :project
  belongs_to :task
  accepts_nested_attributes_for :task
  validates_presence_of :task_id, :project_id
end

class Task < ActiveRecord::Base
  has_many :meetings, :dependent => :destroy
end

プロジェクトは常に存在し、見たくないミーティングが既に存在する可能性があります。タスクは、他のミーティングを通じて他のプロジェクトに属する場合がありますが、この場合、タスクとミーティングは常に新しいものです。

コントローラーでは、新しいアクションでのみ空のレコードを作成します

@project.meetings.build

次のようにデータを保存します。

@project.update_attributes(params[:project])

そしてビューで

- semantic_form_for @project do |f|
  - f.semantic_fields_for :meetings do |m|
    - next unless m.object.new_record?
    = m.semantic_errors :task_id
    - m.object.build_task unless i.object.task
    - m.semantic_fields_for :task do |t|
      - f.inputs do
        = t.input :task_field
        = m.input :meeting_field

フォームを保存しようとすると、「タスクを空白にすることはできません」という検証エラーが表示されます。確かに、Task はまだ保存されていません。検証しようとしていますが、ID がありません。

子レコードの前に孫レコード (タスク) が作成されるようにする簡単でエレガントな方法はありますか?

Meeting モデルで次のようなことを試しました。

before_validation_on_create do |meeting|
  meeting.task.save if meeting.task.valid?
end

これでタスクが保存されたように見えますが、ミーティングはまだ正しい ID を取得していません。同じエラーですが、タスク レコードが作成されます。

私もこれを試しました:

before_validation_on_create do |meeting|
  new_task = meeting.task.save if meeting.task.valid?
  meeting.task = new_task
end

これは ActiveRecord::RecordNotFound "Couldn't find Task with ID=XX for Meeting with ID=" を発生させるという奇妙な動作をします - これは一種の取得ですが、赤ニシンのようです。

また、すべての関係に :inverse_of を追加して、:task_id の代わりに :task を検証してみました。後者は、奇妙なことに失敗しますが、エラー メッセージは表示されないようです。

ここでの私の実際の目標は、複数のタスクを作成し、それぞれが以前に選択したプロジェクトで最初の会議を行うことです...そのため、問題に対して別のアプローチを取ることができます-コントローラーで単純で醜いことを行うか、最初のタスクを作成することができますプロジェクトの after_create での会議。しかし、これはとてもきれいで、すぐに機能します。:task_field と :meeting_field で適切な検証エラーが発生しているという事実は、正しい軌道に乗っていることを意味します。

問題が何であるかはわかりますが、それを解決する方法はわかりません。明らかな何かが欠けているのではないかと思います。

ありがとうございました!

4

1 に答える 1

1

まあ、私はそこにある同様の質問の1つに基づいて解決策を見つけました. 私が見た他のどの回答よりも簡潔な方法で回答できると思います。

:task_id の検証をスキップしますが、タスクが有効な場合のみです! 私が見た他の回答のほとんどはprocを使用していますが、次のようにデリゲートを使用すると読みやすいと思います。

delegate :valid?, :to => :task, :prefix => true, :allow_nil => true
validates_presence_of :task_id, :unless => :task_valid?

また、ウォーターラインの下に別の問題が隠されていました。この場合、「プロジェクト」は実際には保護したい特別な種類のレコードであり、この特別なレコードに対してのみ(意図的に)失敗する検証があり、設定もしました読み取り専用?特別なレコードの場合は true にします。

その特別なレコードを実際に変更しているわけではありませんが、それでも検証する必要があり、それを介して子を更新するために読み取り専用にすることはできません。何らかの理由で、その検証のエラー メッセージが表示されませんでした。それを解決するために、プロジェクトの検証を :on => :create のみに適用し、読み取り専用を取り出しましたか? もの。

しかし、一般的な解決策は、「オブジェクト自体が有効な場合、ビルドされていない属しているオブジェクトの存在を検証しない」ことです。Nil は決して有効ではないため、object_id だけを持っている場合でも検証は機能します。

(回答またはリンクがない限り、誠実な質問に投票しないでください。他の人が他の方法で質問したことは承知しています。他の質問の多くを読みましたが、どれも正確にはそうではないようでした。同じ問題で、解決策が見つかりませんでした。)

于 2012-05-17T20:39:02.333 に答える