web-dev-qa-db-ja.com

Rails形式の複数のオブジェクト

モデル写真の複数のアイテムを1つのフォームで編集したい。フォームで正しく表示する方法とPOSTこれ、およびコントローラーの更新アクションでアイテムを収集する方法は不明です。

これは私が欲しいものです:

<form>
<input name="photos[1][title]" value="Photo with id 1" />
<input name="photos[2][title]" value="Photo with id 2" />
<input name="photos[3][title]" value="Custom title" />
</form>

上記のように、パラメーターは単なる例です。この形式でPOSTこれらの値に最適な方法がわからない。

コントローラーでは、次のようにします。

@photos = Photo.find( params[photos] )
@photos.each do |photo|
    photo.update_attributes!(params[:photos][photo] )
end
49
Espen

Rails 4、これだけ

<%= form_tag photos_update_path do %>
  <% @photos.each do |photo| %> 
    <%= fields_for "photos[]", photo do |pf| %>
      <%= pf.text_field :caption %>
      ... other photo fields
76
mmell

[〜#〜] update [〜#〜]:この回答は、Rails 2、またはカスタムロジックを必要とする特別な制約。

Railsは、これを実現するのにあまり役立ちません。これは標準のビュー規則に反するため、ビュー、コントローラー、さらにはルートで回避策を実行する必要があります。それは面白くない。

Rails wayのマルチモデル形式の取り扱いに関する重要なリソースは Stephen Chuのparams-fooシリーズ 、またはRails 2.3、チェックアウト ネストされたオブジェクトフォーム

Photosetのように、編集中のある種の単一のリソースを定義すると、はるかに簡単になります。 Photosetは、実際のActiveRecordタイプのモデルにすることも、ActiveRecordモデルであるかのようにデータを受け入れてエラーをスローするファサードにすることもできます。

これで、次のようなビューフォームを作成できます。

<%= form_for :photoset do |f|%>
  <% f.object.photos.each do |photo| %>
    <%= f.fields_for photo do |photo_form| %>
      <%= photo_form.text_field :caption %>
      <%= photo_form.label :caption %>
      <%= photo_form.file_field :attached %>
    <% end %>
  <% end %>
<% end %>

モデルは、入ってくる各子の写真を検証し、エラーを集計する必要があります。 クラスに検証を含める方法に関する優れた記事 をご覧ください。次のようになります。

class Photoset
  include ActiveRecord::Validations
  attr_accessor :photos

  validate :all_photos_okay

  def all_photos_okay
    photos.each do |photo|
      errors.add photo.errors unless photo.valid?
    end
  end

  def save
    photos.all?(&:save)
  end

  def photos=(incoming_data)
    incoming_data.each do |incoming|
       if incoming.respond_to? :attributes
         @photos << incoming unless @photos.include? incoming
       else
         if incoming[:id]
            target = @photos.select { |t| t.id == incoming[:id] }
         end
         if target
            target.attributes = incoming
         else
            @photos << Photo.new incoming 
         end
       end
    end
  end

  def photos
     # your photo-find logic here
    @photos || Photo.find :all
  end
end

Photosetのファサードモデルを使用することで、コントローラーとビューロジックをシンプルかつ簡単に保ち、専用モデルの最も複雑なコードを予約できます。このコードはすぐに実行できるとは限りませんが、いくつかのアイデアが得られ、質問を解決するための正しい方向を示してくれることを願っています。

48

Railsにはこれを行う方法があります-それがいつ導入されたのかわかりませんが、基本的にここに説明されています: http://guides.rubyonrails.org/form_helpers.html#using-form-helpers =

親オブジェクトがない場合に設定を適切に変更するには少し手間がかかりましたが、これは正しいようです(基本的にはgamovの答えと同じですが、よりクリーンで、「新しい」レコードを混在させることはできません) 「更新」レコード):

<%= form_tag photos_update_path do %>
  <% @photos.each do |photo| %> 
    <%= fields_for "photos[#{photo.id}]", photo do |pf| %>
      <%= pf.text_field :caption %>
        ... [other fields]
    <% end %>
  <% end %>
<% end %>

コントローラーでは、params[:photos]、キーは写真ID、値は属性ハッシュです。

18
mltsy

「モデル名[]」構文を使用して、複数のオブジェクトを表すことができます。

ビューでは、モデル名として「photo []」を使用します。

<% form_for "photo[]", :url => photos_update_path do |f| %>
  <% for @photo in @photos %>
    <%= render :partial => "photo_form", :locals => {f => f} %>
    <%= submit_tag "Save"%>
  <% end %>
<% end %>

これは、説明したとおりに入力フィールドに入力されます。

コントローラーでは、一括更新を行うことができます。

def update
  Photo.update(params[:photo].keys, params[:photo].values)
  ...
end 
15
Glen

実際、Turadgが述べたように、Glenの答えで新しいレコードと既存のレコードを混在させると、Rack(Rails 3.0.5)は失敗します。 fields_forを手動で動作させることにより、これを回避できます。

<%= form_tag photos_update_path do %>
  <% @photos.each_with_index do |photo,i| %> 
    <%= fields_for 'photos[#{i}]', photo do |pf| %>
       <%= pf.hidden_field :id %>
        ... [other photo fields]
  <% end %>
<% end %>

あなたが私に尋ねるとこれはかなりugいですが、それは私が新しいレコードと既存のレコードを混合しながら複数のレコードを編集するために見つけた唯一の方法です。ここでのコツは、レコードの配列を持つ代わりに、paramsハッシュがハッシュの配列(i、0、1、2などで番号付け)とレコードハッシュのidを取得することです。 Railsは、それに応じて既存のレコードを更新し、新しいレコードを作成します。

もう1つの注意:コントローラー内の新規および既存のレコードを個別に処理する必要があります(:id.present?

8
gamov