-1

Is there a way to do fields_for a has_many association without looping through all the associated items?


I have a very similar situation to that discussed here:

I have a Person model, with several related models (Address, Phone number, Jobtitle...), and on the form I'd only like to create new records. The new record should be pre-filled from the existing record. I have a helper function which catches the right data, I just want fields_for to display that without having to go through the do loop.

Community
  • 1
  • 1
thenapking
  • 135
  • 12

1 Answers1

2

Here's what you need:

#app/models/person.rb
class Person < ActiveRecord::Base
   has_many :addresses
   has_many :phone_numbers
   has_many :job_titles

   accepts_nested_attributes_for :addresses, :phone_numbers, :job_titles
end

#app/controllers/people_controller.rb
class PeopleController < ApplicationController
   def edit
       @person = Person.find params[:id]
       @person.addresses.build #-> this adds onto the existing "addresses" for that person
   end
end

This will allow you to use the following:

#app/views/people/edit.html.erb
<%= form_for @person do |f| %>
   <%= f.fields_for :addresses do |a| %>
       <% if a.object.new_record? %>
          <%= a.text_field :street %>
          <%= a.text_field :town %>
       <% end %>
   <% end %>
   <%= f.submit %>
<% end %>

This should only output the inputs for the new associative object (the one you built). It's a bit hacky, but I get your problem -- you only want to show the new inputs (the existing ones should not show).

Saving this will add an associative record to your Person object. The existing ones will still exist.

Richard Peck
  • 73,250
  • 8
  • 84
  • 139
  • This is essentially what I'm doing, although I found that using `a.object.new_record?` didn't work in all situations and would wipe out data during the form round trip if validation failed. I'm selecting the correct data into a variable called data_object and then checking `a.object.attributes == data_object.attributes`. This is the 'looping' behaviour I'd like to remove. – thenapking Nov 09 '15 at 16:14
  • You mean you want to keep your code lean, thus removing the looping functionality which makes it scan through the already assigned attributes? – Richard Peck Nov 09 '15 at 19:32
  • You've hit the nail precisely on the head. The problem is that in this project the People model has 16 associations, each of which might have 5 or 6 historical records. I've got a page load time north of 10s because of this enumeration :( – thenapking Nov 10 '15 at 13:28
  • I think the solution may be to introduce a `has_one :new_address `relationship. My problem with this approach before was that you can have two addresses of different types (home and office, say) and so this meant this approach was a non starter. I am refactoring for STI in this case, and we'll see if it works. – thenapking Nov 10 '15 at 14:04
  • Yes, please let me know if it does - I am interested! Thanks for the heads-up; I'm sure there must be a way to do this... the difference between the new & populated objects are pretty stark – Richard Peck Nov 10 '15 at 14:05
  • 1
    It turns out you can't have a `has_one :new_address` relationship if you have a `has_many :addresses` relationship, as doing `@person.build_new_address` will try to update the last historical address record, alter it's PK and set its FK to NULL amongst other things. – thenapking Nov 10 '15 at 17:29
  • 1
    Please remove the one if the "if" on the line " if if a.object.new_record?". Other than that, that was exactly what I was looking for :-) Cheers – Eduardo López Sep 30 '16 at 11:11