我想以一种形式编辑模型照片的多个项目。我不确定如何使用表单正确显示和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>
参数只是一个例子,就像我上面所说:我不确定以这种形式发布这些值的最佳方法。
在控制器中,我希望这样:
@photos = Photo.find( params[photos] )
@photos.each do |photo|
photo.update_attributes!(params[:photos][photo] )
end
答案 0 :(得分:75)
在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
答案 1 :(得分:48)
Rails不会帮助你做很多事情。它违反了标准视图约定,因此您必须在视图,控制器甚至路径中进行变通方法。这没什么好玩的。
处理多模型形式Rails方式的关键资源是Stephen Chu's params-foo series,或者如果您使用的是Rails 2.3,请查看Nested Object Forms
如果您定义正在编辑的某种单一资源(如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 %>
您的模型应该验证每个孩子的照片并汇总他们的错误。您可以查看a good article on how to include Validations in any class。它可能看起来像这样:
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的外观模型,您可以使控制器和视图逻辑简单明了,为专用模型保留最复杂的代码。这段代码可能不会开箱即用,但希望它会给你一些想法,并指出你正确的方向来解决你的问题。
答案 2 :(得分:18)
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,值是属性哈希值。
答案 3 :(得分:15)
您可以使用“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 %>
这将填充输入字段,就像您描述的那样。
在您的控制器中,您可以进行批量更新。
def update
Photo.update(params[:photo].keys, params[:photo].values)
...
end
答案 4 :(得分:8)
事实上,正如Turadg所说,如果你混合使用新的&amp; amp; 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 %>
如果你问我,这是非常难看的,但这是我发现在混合新记录和现有记录时编辑多个记录的唯一方法。 这里的技巧是,params散列不是拥有一个记录数组,而是获取一个哈希数组(用i,0,1,2等编号)和记录哈希中的id。 Rails将相应地更新现有记录并创建新记录。
还有一点需要注意:您仍然需要单独处理控制器中的新记录和现有记录(检查是否:id.present?)