views:

281

answers:

2

Given these relationships:

class Account < ActiveRecord::Base
  has_many :employments
  has_many :people, :through => :employments
  accepts_nested_attributes_for :employments
end

class Employment < ActiveRecord::Base
  belongs_to :account
  belongs_To :person
end

I'm trying to list the employment records for an account:

<% form_for @account do |f| -%>
  <% f.fields_for :employments do |e| -%>
    <%= render :partial => 'employment', :collection => @account.employments, :locals => { :f => e } %>
  <% end -%>
<% end -%>

I've verified that the employment table in @account contains two records, but I get four copies of the partial because it iterates employments twice:

Employment Load (1.0ms)   SELECT * FROM [employments] WHERE ([employments].account_id = 1) 
Person Load (1.3ms)   SELECT * FROM [people] WHERE ([people].[id] = 2) 
Rendered accounts/_employment (17.9ms)
Person Load (1.5ms)   SELECT * FROM [people] WHERE ([people].[id] = 1) 
Rendered accounts/_employment (5.1ms)
Rendered accounts/_employment (2.2ms)
Rendered accounts/_employment (2.1ms)

Can anybody explain why that would happen?


Here's some additional information:

The _employment.html.erb partial:

<div class="employment">
  <span class="name"><%= link_to h(employment.person.name), person_path(employment.person) %></span>
  <span class="role"><%=h employment.role %></span>
  <span class="commands"><%= remove_child_link "Remove", f %></span>
</div>

remove_child_link is the only place I need to generate a form field at. It creates the _delete field for the record and wires up a remove link that changes the value to '1'. The 'role' property may also be editable, though. The important thing is I don't want all of the fields to be editable.

The accounts_controller actions for this view:

def edit
  @account = Account.find(params[:id])
end

def update
  @account = Account.find(params[:id])

  respond_to do |format|
    if @account.update_attributes(params[:account])
      flash[:notice] = "#{@account.business_name} was successfully updated."
      format.html { redirect_to @account }
    else
      format.html { render :action => "edit" }
    end
  end
end


Ben got me going in the right direction. Some runtime inspection reveals that the record is stored in the object variable (which I already knew, but in a different context). So I can rewrite the fields_for clause as:

<% form_for @account do |f| -%>
  <% f.fields_for :employments do |e| -%>
    <div class="employment">
      <span class="name"><%= link_to h(e.object.person.name), person_path(e.object.person) %></span>
      <span class="role"><%=h e.object.role %></span>
      <span class="commands"><%= remove_child_link "Remove", e %></span>
    </div>
  <% end -%>
<% end -%>
+2  A: 

Its rendering that partial for each field in the employments model - when really you want to do it once for each employment record. That is, remove the iteration over the fields_for:

<% form_for @account do |f| -%>
   <%= render :partial => 'employment', :collection => @account.employments %>
<% end -%>
Cody Caughlan
fields_for is an iterator? I though it just changes the form builder's name generation.
Adam Lassek
+1  A: 

You are correct to use fields_for, but it will render what's inside of it for each employment, so remove the :collection parameter from render :partial. Instead, use nested forms by putting this in your Account model:

accepts_nested_attributes_for :employments

read more about nested forms here: http://ryandaigle.com/articles/2009/2/1/what-s-new-in-edge-rails-nested-attributes

Ben
I'm already using `accepts_nested_attributes_for` in there, sorry I forgot to include it in my example. But when I remove the `:collection` parameter, it tries to evaluate `nil.person` in the partial.
Adam Lassek
Does it work when you don't use a partial? Just sticking the nested fields in the `fields_for`?
Ben
Rather than just form fields, I want to display the existing employment records with only certain fields editable, the delete field in particular. Is it possible to display fields as plain html inside `field_for`? I was using `render :partial` because that gave me a local variable for each record.
Adam Lassek
Yeah you can just stick output in <p><%= var %></p> or whatever. Please post your partial, and the controller action for this request.
Ben
I get `undefined local variable` when I try that. How do I access the properties?
Adam Lassek
So you aren't displaying ANY form fields for those objects at all? Why did you start using `fields_for` to begin with?
Ben