2

私はこれを自分で理解するのに苦労しているので、うまく説明できることを願っています。ネストされたモデルを持つフォームがあります。簡略化したバージョンは次のとおりです (Rails 3.0.13 を使用)。

#parent.rb

class Parent < ActiveRecord::Base
  has_many :children, :dependent => destroy
  accepts_nested_attributes_for :children, :allow_destroy => true
  validates_presence_of :thing
  validate :children_unique

  def children_unique
    errors[:base] << "You have the same child listed more than once!" if self.children.map{|x| [child.name, child.age]} != self.children.map{|x| [child.name, child.age]}.uniq
  end
end


#child.rb

class Child < ActiveRecord::Base
  belongs_to :parents
end

#parents_controller.rb

class ParentsController < ApplicationController
  load_and_authorize_resource :parent   #Using cancan for authorization. This calls @parent = Parent.find(params[:id]); authorize! :update, @parent; at the start of the update method

  def update
    if @parent.update_attributes(params[:operation])
      redirect_to @parent.admission, :notice => 'Operation was updated successfully'
    else
      flash.now[:warning] = "Parent was NOT updated!"
      render :action => "edit"
    end
  end
end

これまでのところすべてかなり標準的です。私のフォームもかなり標準的な方法で設定されています。parent_form.fields_for :childrenfields_for ブロック内で子のパーシャルを呼び出してレンダリングします。各子部分フォームには削除リンクが含まれており、クリックすると javascript を使用して隠しフィールドが設定されるため、_destroy の属性が「1」に設定され、部分フォームが非表示になります。

これはほとんどの場合うまく機能しますが、私が見つけた奇妙な問題は次のとおりです。

既に 2 人の子を持つ既存の親を編集している場合、それらの子の 1 つを削除してから、「もの」を空白に設定すると、フォームは期待どおりに検証に失敗し (「もの」が存在しないため)、編集ビューが再レンダリングされます。その結果、削除した子が再び表示されます。その非表示の _destroy フィールドは「true」に設定されており、「thing」に再度入力してフォームを送信すると、更新された親には子が 1 つしかありません。

ネストされた子 div に条件付きスタイル タグを追加することでこれに対処し<div class='fields'<%= " style='display: none;'" if f.object._destroy %>>、レコードを更新する前に削除された場合は表示されないようにしました。これは目的を達成しますが、これを行って空の「もの」フィールドを修正せずにフォームを送信すると、モデルは再び検証に失敗し、表示される次の編集フォームで、以前に削除されたものと同じ新しい子を追加します「もの」フィールドに入力するchildren_uniqueと、同一の子の最初の _delete 属性が「true」に設定されていても、モデルは検証に失敗します。

明らかに、私はここで自分自身を結びつけており、膨大な数の代替アプローチと修正を試みましたが、これがこれまでに思いついた最高のものです. それは非常に近いですが、実際にはおそらく実際には起こらないこの奇妙なエッジケースがまだありますが、これは、コントローラーとモデルが相互作用する方法をよく理解していないことを示唆しています.

誰かが私を正すことができますか?

4

3 に答える 3

1

削除リンクは、子をすぐに削除してページからマークアップを削除するコントローラーへの ajax 呼び出しにする必要があります。

于 2012-06-10T10:45:13.697 に答える
1

何が起こっているのかを理解するには、まず、Rails がデータベースとの対話をどのように処理するかを確認する必要があります。つまり、Rails はすべてのデータベース インタラクションをトランザクションにラップするため、何かが失敗した場合、すべてが元に戻されます。そのため、検証エラーが発生すると、ログに "ROLLBACK" が表示されます。変更をコミットしようとしましたが、エラーが発生したためロールバックされました。大丈夫です。

同様に、親レコードと子レコードを同じフォームで操作する場合、すべてトランザクションで処理されます。これは実際には素晴らしいことです。何かが失敗した場合、いくつかの変更が行われ、1 つが失敗することは望ましくありません。

true に設定_destroyすると、その子レコードを破棄するようにマークするだけです。ただし、この破壊はすぐには起こりません。代わりに、子は親が保存されるまで残ります ( savetrue を返します)。あなたの場合、これは起こっていませんsave。検証エラーのために呼び出しが失敗しています。ただし、これは良いことです。親レコードの保存に失敗しても、子レコードを削除する必要はありません。

そうは言っても、私の推奨は、これらの子レコードを でラップし、属性に設定されてdivいるデータ要素を追加することです。削除リンクがクリックされるたびに true に設定し、ページが読み込まれるたびに、true に設定されたすべてのが非表示になるようにします。data-destroy_destroydivdata-destroy

これが、ボンネットの下で何が起こっているかを理解するのに役立つことを願っています!

于 2012-06-11T00:05:25.437 に答える