Ruby-on-rails Rails 表单中的多个对象
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/972857/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Multiple objects in a Rails form
提问by Espen
I want to edit multiple items of my model photo in one form. I am unsure of how to correctly present and POST this with a form, as well as how to gather the items 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>
The parameters are just an example, like I stated above: I am not sure of the best way to POST these values in this form.
参数只是一个例子,就像我上面所说的:我不确定以这种形式发布这些值的最佳方式。
In the controller I want to something like this:
在控制器中,我想要这样的东西:
@photos = Photo.find( params[photos] )
@photos.each do |photo|
photo.update_attributes!(params[:photos][photo] )
end
回答by mmell
In Rails 4, just this
在 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
回答by austinfromboston
UPDATE: This answer applies to Rails 2, or if you have special constraints that require custom logic. The easy cases are well addressed using fields_for as discussed elsewhere.
更新:此答案适用于 Rails 2,或者如果您有需要自定义逻辑的特殊约束。如别处讨论的那样,使用 fields_for 可以很好地解决简单的情况。
Rails isn't going to help you out a lot to do this. It goes against the standard view conventions, so you'll have to do workarounds in the view, the controller, even the routes. That's no fun.
Rails 不会帮助你做很多事情。它违反了标准视图约定,因此您必须在视图、控制器甚至路由中进行变通。那不好玩。
The key resources on dealing with multi-model forms the Rails way are Stephen Chu's params-foo series, or if you're on Rails 2.3, check out Nested Object Forms
以 Rails 方式处理多模型表单的关键资源是Stephen Chu 的 params-foo 系列,或者如果您使用的是 Rails 2.3,请查看嵌套对象表单
It becomes much easier if you define some kind of singular resource that you are editing, like a Photoset. A Photoset could be a real, ActiveRecord type of model or it can just be a facade that accepts data and throws errors as if it were an ActiveRecord model.
如果您定义某种正在编辑的单一资源(例如 Photoset),这会变得容易得多。Photoset 可以是一个真实的 ActiveRecord 类型的模型,也可以只是一个外观,它接受数据并像 ActiveRecord 模型一样抛出错误。
Now you can write a view form somewhat 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 validate each child Photo that comes in and aggregate their errors. You may want to check out a good article on how to include Validations in any class. It could look something like this:
您的模型应该验证进入的每个子 Photo 并汇总它们的错误。您可能想查看一篇关于如何在任何类中包含验证的好文章。它可能看起来像这样:
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
By using a facade model for the Photoset, you can keep your controller and view logic simple and straightforward, reserving the most complex code for a dedicated model. This code probably won't run out of the box, but hopefully it will give you some ideas and point you in the right direction to resolve your question.
通过为 Photoset 使用外观模型,您可以保持控制器和视图逻辑简单明了,为专用模型保留最复杂的代码。这段代码可能不会开箱即用,但希望它会给您一些想法,并为您指明解决问题的正确方向。
回答by mltsy
Rails does have a way to do this - I don't know when it was introduced, but it's basically described here: http://guides.rubyonrails.org/form_helpers.html#using-form-helpers
Rails 确实有办法做到这一点 - 我不知道它是什么时候引入的,但基本上在这里描述:http: //guides.rubyonrails.org/form_helpers.html#using-form-helpers
It took a bit of fiddling to alter the configuration properly for the case where there's no parent object, but this seems to be correct (it's basically the same as gamov's answer, but cleaner and doesn't allow for "new" records mixed in with the "update" records):
在没有父对象的情况下,正确更改配置需要一些麻烦,但这似乎是正确的(它与 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 %>
In your controller, you'll end up with a hash in params[:photos], where the keys are photo IDs, and the values are attribute hashes.
在您的控制器中,您最终会得到一个散列 in params[:photos],其中键是照片 ID,值是属性散列。
回答by Glen
You can use "model name[]" syntax to represent multiple objects.
您可以使用“模型名称[]”语法来表示多个对象。
In view, use "photo[]" as a 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 populate input fields just like you described.
这将像您描述的那样填充输入字段。
In your controller, you can do bulk updates.
在您的控制器中,您可以进行批量更新。
def update
Photo.update(params[:photo].keys, params[:photo].values)
...
end
回答by gamov
Indeed, as Turadg mentioned, Rack (Rails 3.0.5) fails if you mix new & existing records in Glen's answer. You can work around this by making fields_for work manually:
事实上,正如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 %>
This is pretty ugly if you ask me, but it's the only way I found to edit multiple records while mixing new and existing records. The trick here is that instead of having an array of records, the params hash gets a array of hashes (numbered with i, 0,1,2, etc) AND the id in the record hash. Rails will update the existing records accordingly and create the new ones.
如果你问我,这很丑陋,但这是我发现在混合新记录和现有记录的同时编辑多个记录的唯一方法。这里的技巧是,params 哈希不是拥有一组记录,而是获取一组哈希(用 i、0、1、2 等编号)和记录哈希中的 id。Rails 将相应地更新现有记录并创建新记录。
One more note: You still need to process the new and existing records in the controller separately (check if :id.present?)
再注意一点:您仍然需要分别处理控制器中的新记录和现有记录(检查:id.present?)

