1

ユーザーがいくつかのレコードを更新できるフォームを作成しようとしています。ただし、すべてのフィールドを更新できるわけではないので、フォームに対してモデルを更新するために、(今のところコントローラーで)明示的な処理を実行します。これが私がそれをやろうとしている方法です:

家族モデル:

class Family < ActiveRecord::Base
  has_many :people, dependent: :destroy
  accepts_nested_attributes_for :people, allow_destroy: true, reject_if: ->(p){p[:name].blank?}
end

コントローラ内

def check
  edited_family = Family.new(params[:family])
  #compare to the one we have in the db
  #update each person as needed/allowed
  #save it
end

形:

= form_for current_family, url: check_rsvp_path, method: :post do |f|
  = f.fields_for :people do |person_fields|
    - if person_fields.object.user_editable
      = person_fields.text_field :name, class: "person-label"
    - else
      %p.person-label= person_fields.object.name

問題は、Family.new(params[:family])データベースから人々を引き出そうとすることだと思います、そして私はこれを手に入れます:

ActiveRecord::RecordNotFound in RsvpsController#check

Couldn't find Person with ID=7 for Family with ID=

つまり、ネストされたフォームにファミリIDのフィールドを追加していないためです。これは可能だと思いますが、データベースから何かをロードするために実際には必要ないので、むしろそうではありません。必要なデータのパラメータハッシュを自分で掘り下げるだけでこれを回避することもできますが、それはスムーズではありません。paramsハッシュからオブジェクトを作成し、それを操作するのが最も良いようです。

もっと良い方法はありますか?ネストされたオブジェクトを作成するにはどうすればよいですか?

4

2 に答える 2

1

これらのパラメーターを使用して新しいファミリー オブジェクトをインスタンス化するのではなく、check rsvp アクションのメンバー ルートを作成することをお勧めします。ルートは次の形式になります。

resources :families do
  member do
    post 'check_rsvp'
  end
end

form_for は current_family の ID を自動的に渡すため、チェック アクションは次のようになります。

def check
  edited_family = Family.find(params[:id])
  # ...
end

これは、ファミリ id パラメータを自分で追加することと機能的に同等に思えるかもしれませんが、それよりも、または他のパラメータに基づいて新しいファミリ オブジェクトをインスタンス化するよりも優れていると思います。

  1. それはより慣用的です (The Rails Way™)。
  2. コードが少ないです。
  3. edit_family オブジェクトの参照透過性が得られます。これにより、既に永続化されているオブジェクトの属性に基づいて新しい Active Record オブジェクトをアドホックにインスタンス化するために発生する微妙なバグの可能性が減少します。
于 2012-12-01T20:03:43.703 に答える
0

私は@244anの提案を受け入れることになりました:

class Person < ActiveRecord::Base
  belongs_to :family

  def temp_id
    @temp_id || self.id
  end

  def temp_id=(new_id)
    @temp_id = new_id
  end
end

フォーム:

= form_for current_family, url: check_rsvp_path, method: :post, html: {class: "form-inline"} do |f|
  #people-list
    = f.fields_for :people, include_id: false do |person_fields|
      = person_fields.hidden_field :temp_id
      #rest of the form here

次に、私のコントローラーで:

def check
    @edited_family = Family.new(params[:family])

    current_people = Hash[current_family.people.map{|p| [p.id, p]}]

    @edited_family.people.each do |person|
      current_person = current_people[person.temp_id.to_i]

      next unless current_person

      current_person.name = person.name if current_person.user_editable
      current_person.attending = person.attending
      current_person.save!
    end

    current_family.responded = true
    current_family.save!

  end

そのフィールドをモデルに追加した理由は、フィールドの名前を変更するための良い方法を思い付くことができなかったためhidden_fieldです。

とにかく、それはスーパーハッキーに感じますが、それは機能します。私が本当に望んでいたのは、newIDを持っていても子オブジェクトをDBに一致させようとしないように指示する方法でした。

于 2012-12-04T00:59:04.673 に答える