Rails表单中的多个对象

我想以一种forms编辑我的模型照片的多个项目。 我不确定如何正确地呈现和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> 

参数只是一个例子,就像我上面所说的那样:我不确定以这种forms发布这些值的最佳方式。

在控制器中我想要这样的东西:

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

在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 

更新 :这个答案适用于Rails 2,或者如果你有特殊的约束需要自定义的逻辑。 正如其他地方所讨论的,使用fields_for很容易解决这个问题。

Rails不会帮你做很多事情。 它违背了标准的视图惯例,所以你必须在视图,控制器,甚至路线中做变通。 那没什么好玩的

处理多模型的关键资源forms是Rails方式,如Stephen Chu的params-foo系列 ,或者如果你使用Rails 2.3,请查看嵌套对象表单

如果您定义某种您正在编辑的单一资源,就像Photoset一样,变得更容易。 Photoset可以是一个真实的ActiveRecordtypes的模型,也可以是一个接受数据并引发错误的外观,就像它是一个ActiveRecord模型一样。

现在你可以写一个视graphics式,像这样:

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

你的模型应该validation每个孩子的照片,并汇总他们的错误。 您可能想查看一篇关于如何在任何课程中包含validation的好文章 。 它可能看起来像这样:

 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的外观模型,您可以保持控制器和视图逻辑的简单直接,为专用模型保留最复杂的代码。 这个代码可能不会用完,但希望它会给你一些想法,并指出你在正确的方向来解决你的问题。

Rails确实有办法做到这一点 – 我不知道它是什么时候引入的,但基本上是这样描述的: http : //guides.rubyonrails.org/form_helpers.html#using-form-helpers

在没有父对象的情况下,需要花一点时间修改configuration,但是这似乎是正确的(它基本上与gamov的答案相同,但是更清晰,并且不允许混入“新”logging“更新”logging):

 <%= 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,值是属性散列值。

您可以使用“model name []”语法来表示多个对象。

鉴于此,请使用“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 %> 

这将像您所描述的那样填充input字段。

在你的控制器中,你可以做批量更新。

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

事实上,正如Turadg所说的,如果在Glen的答案中混合新的和现有的logging,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 %> 

如果你问我,这是非常难看的,但这是我发现在混合新logging和现有logging时编辑多个logging的唯一方法。 这里的诀窍是,而不是有一个logging数组,params哈希得到一个散列数组(编号与我,0,1,2等)和logging哈希id。 Rails将相应地更新现有的logging并创build新的logging。

还有一点需要注意:您仍然需要分别处理控制器中的新logging和现有logging(请检查:id.present?)