【问题标题】:Multiple objects in a Rails formRails 表单中的多个对象
【发布时间】:2010-11-01 15:39:39
【问题描述】:

我想以一种形式编辑我的模特照片的多个项目。我不确定如何使用表单正确呈现和发布此内容,以及如何在控制器的更新操作中收集项目。

这就是我想要的:

<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

【问题讨论】:

    标签: ruby-on-rails forms post


    【解决方案1】:

    您可以使用“模型名称[]”语法来表示多个对象。

    在视图中,使用“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 
    

    【讨论】:

    • 请注意,如果您混合新的和现有的记录,Rack 将显示:TypeError (expected Hash (got Array) for param 'photo')
    • photo_form 部分会是什么样子?
    【解决方案2】:

    更新:此答案适用于 Rails 2,或者如果您有需要自定义逻辑的特殊约束。正如其他地方所讨论的,使用 fields_for 可以很好地解决简单的情况。

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

    【讨论】:

    • 感谢您的详细解答。我很惊讶 Rails 不支持这一点。另一种选择是使用多种形式并使用 ajax 发布它们。两者都不是理想的解决方案。
    • austinfromboston 的答案是最优雅的,使用外观模型,但他在
    【解决方案3】:

    确实,正如 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 %>
    

    如果您问我,这非常难看,但这是我发现在混合新记录和现有记录时编辑多条记录的唯一方法。 这里的诀窍是,params 散列得到一个散列数组(编号为 i、0、1、2 等)和记录散列中的 id,而不是记录数组。 Rails 将相应地更新现有记录并创建新记录。

    再说明:你仍然需要分别处理控制器中的新记录和现有记录(检查是否:id.present?)

    【讨论】:

      【解决方案4】:

      在 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
      

      【讨论】:

      • 但是如果你想传递值来填充输入呢?
      • fields_for 方法用已知值@Gediminas 预填充字段
      • @shmag18 这是一个 Rails path helper
      • @shlomo_maghen 取决于您config/routes.rb,当您运行rake routes 时,您可以看到第一列prefix,您可以添加_path 以从应用程序中使用它,或者将_url 添加到从外部来源使用它。
      • 这段代码中的"photos[ ]"在“fields_for”标签之后做了什么?
      【解决方案5】:

      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,值是属性哈希。

      【讨论】:

      • 我知道我来晚了,但我喜欢你的样子和@mmell 的答案。不过有一个问题 - 提交按钮在哪里适合?
      • @SRack 不要忘记你总是可以只写 HTML。如果你想在这里使用 rails 助手,你可以使用
      猜你喜欢
      • 2014-10-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-22
      • 1970-01-01
      相关资源
      最近更新 更多