Multiple Rails Form Objects

I want to edit several elements of my photo model in one form. I am not sure how to correctly represent and POST this with the form, as well as how to collect elements in the update action in the controller.

This is what I want:

<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> 

Parameters are just an example, as I said above: I'm not sure of the best way to POST these values ​​in this form.

In the controller, I want something like this:

 @photos = Photo.find( params[photos] ) @photos.each do |photo| photo.update_attributes!(params[:photos][photo] ) end 
+47
post ruby-on-rails forms
Jun 09 '09 at 10:40
source share
5 answers

In Rails 4, only this

 <%= form_tag photos_update_path do %> <% @photos.each do |photo| %> <%= fields_for "photos[]", photo do |pf| %> <%= pf.text_field :caption %> ... other photo fields 
+70
Dec 27 '13 at 15:37
source share

UPDATE . This answer applies to Rails 2, or if you have special restrictions that require user logic. Mild cases are well resolved using name_fields, as discussed elsewhere.

Rails will not help you do much. This is contrary to standard viewing conventions, so you have to do workarounds in the view, controller, even routes. This is not fun.

Key resources for working with Rails multimodel forms are Stephen Chu params-foo series , or if you are on Rails 2.3 check out Nested Object Forms

It becomes much easier if you define some particular resource that you are editing, for example, Photoset. A Photoset may be a real model such as ActiveRecord, or it may simply be a facade that receives data and throws errors, as if it were an ActiveRecord model.

Now you can write a view form like this:

 <%= 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 %> 

Your model should check every child who enters the system and aggregate his mistakes. You can check out a good article on how to include checks in any class . It might look something like this:

 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 

Using the facade model for Photoset, you can easily and simply save the controller logic and view it by reserving the most complex code for the selected model. This code will probably not be exhausted, but hopefully it will give you some ideas and point you in the right direction to solve your question.

+45
Jun 10 '09 at 0:43
source share

Rails has a way to do this - I don’t know when it was introduced, but is mainly described here: http://guides.rubyonrails.org/form_helpers.html#using-form-helpers

It took a little play to reconfigure correctly for the case when there is no parent, but it seems to be correct (this is basically the same as gamov's answer, but cleaner and does not allow β€œnew” entries mixed with β€œupdate” entries) :

 <%= 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 %> 

In your controller, you will get a hash in params[:photos] , where the keys are the identifiers of the photos and the values ​​are the hashes of the attributes.

+17
Apr 01 '13 at
source share

You can use the syntax of the model name [] to represent several objects.

In view, use "photo []" as the model name.

 <% 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 %> 

This will fill in the input fields as you described.

In your controller, you can perform bulk updates.

 def update Photo.update(params[:photo].keys, params[:photo].values) ... end 
+14
Jul 22 '10 at 3:16
source share

In fact, as Turaj said, Rack (Rails 3.0.5) fails if you mix new and existing entries in Glen's answer. You can get around this by making fields_for work manually:

 <%= 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 %> 

This is pretty ugly if you ask me, but this is the only way I found to edit multiple records while mixing new and existing records. The trick here is that instead of an array of entries, the hash hash receives an array of hashes (numbered with i, 0,1,2, etc.) and the identifier in the hash entry. Rails updates existing records accordingly and creates new ones.

One more note: you still need to process new and existing entries in the controller separately (check if there is: id.present?)

+7
Mar 22 2018-11-11T00:
source share



All Articles