Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

fields_for ignores supplied collection (ActiveRecord::Relation)

#5795

When supplying a collection for use with the fields_for helper method, it will only be used if the object is an Array. It should also work with an ActiveRecord::Relation.

Example

See the example, taken from http://railsapi.com/doc/rails-v3.0.0/classes/ActionView/Helpers/For...)

edit.html.erb

<%= form_for @person do |person_form| %>
    ...
    <%= person_form.fields_for :projects, @active_projects do |project_fields| %>
      Name: <%= project_fields.text_field :name %>
    <% end %>
<% end %>

The @active_projects variable is only used when it is forced into an Array:

people_controller.rb

  def edit
    ...
    # when using this scenario, the @active_projects collection is not used
    @active_projects = @person.projects.where(:active => true)

    # when forcing the conversion to an array, @active_projects is used
    @active_projects = @person.projects.where(:active => true).all
  end

Note: when the @active_projects variable is not used, the default @person.projects association is used.

Reported by Nigel Ramsay · October 12th, 2010 @ 11:26 PM

State: resolved
Milestone: 3.0.2
Assigned to: José Valim José Valim
Importance: Low

Activity

  1. Nigel Ramsay
    Nigel Ramsay
    • Tag changed from fields_for, form_helper, relation to 3.0.0, fields_for, form_helper, relation

    October 12th, 2010 @ 11:30 PM

  2. Nigel Ramsay
    Nigel Ramsay

    This problem originates in form_helper.rb:1159:

    case record_or_name_or_array
      when String, Symbol
        if nested_attributes_association?(record_or_name_or_array)
          return fields_for_with_nested_attributes(record_or_name_or_array, args, block)
        else
          name = "#{object_name}#{index}[#{record_or_name_or_array}]"
        end
      when Array # 1159
        object = record_or_name_or_array.last
        name = "#{object_name}#{index}[#{ActiveModel::Naming.singular(object)}]"
        args.unshift(object)
      else
        object = record_or_name_or_array
        name = "#{object_name}#{index}[#{ActiveModel::Naming.singular(object)}]"
        args.unshift(object)
      end
    

    rather than checking for an Array class, we should really be checking for an "array-like" class.

    October 12th, 2010 @ 11:41 PM

  3. Nigel Ramsay
    Nigel Ramsay

    Perhaps the replacement check should be similar to that used in partials.rb:302

      if @object.respond_to?(:to_ary)
    

    October 13th, 2010 @ 12:06 AM

  4. Ryan Bigg
    Ryan Bigg
    • State changed from new to incomplete
    • Importance changed from to Low

    Please submit a failing test case for this issue as per this guide: http://guides.rubyonrails.org/contributing_to_rails.html

    October 13th, 2010 @ 06:27 AM

  5. Ryan Bigg
    Ryan Bigg
    • Tag cleared.

    Automatic cleanup of spam.

    October 19th, 2010 @ 08:27 AM

  6. Neeraj Singh
    Neeraj Singh
    • State changed from incomplete to open
    • Milestone set to 3.0.2
    • Assigned user changed from Michael Koziarski to Neeraj Singh

    Code fix is here. I tested it and it works. However writing writing the test is taking some time. Hopefully will finish the test tomorrow. I will submit a proper patch when test is ready. In the meantime here is the code change.

            def fields_for_with_nested_attributes(association_name, args, block)
              name = "#{object_name}[#{association_name}_attributes]"
              options = args.extract_options!
              association = args.shift
              association = convert_to_model(association)
    
              debugger
    
              if association.respond_to?(:persisted?)
                association = [association] if @object.send(association_name).is_a?(Array)
              #elsif !association.is_a?(Array)
              elsif !association.respond_to?(:to_ary)
                association = @object.send(association_name)
              end
    
              #if association.is_a?(Array)
              if association.respond_to?(:to_ary)
                explicit_child_index = options[:child_index]
                output = ActiveSupport::SafeBuffer.new
                association.each do |child|
                  output << fields_for_nested_model("#{name}[#{explicit_child_index || nested_child_index(name)}]", child, options, block)
                end
                output
              elsif association
                fields_for_nested_model(name, association, options, block)
              end
            end
    

    November 11th, 2010 @ 05:53 AM

  7. Neeraj Singh
    Neeraj Singh
    • Assigned user changed from Neeraj Singh to José Valim
    • Tag set to patched

    Attached is code patch with test.

    November 11th, 2010 @ 03:08 PM

  8. Repository
  9. Repository