开发者

Why is this render :partial line iterating my collection twice?

开发者 https://www.devze.com 2022-12-12 14:19 出处:网络
Given these relationships: class Account < ActiveRecord::Base has_many :employments has_many :people, :through => :employments

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.1m开发者_如何学Cs)

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 -%>


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 -%>


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

0

精彩评论

暂无评论...
验证码 换一张
取 消