Rails 3 Controller:从复合表单中保存相关记录

时间:2013-12-06 08:27:23

标签: ruby-on-rails-3 controller model-associations

我正在使用Rails 3并且有一个表单,它使用fields_for合并来自多个相关记录的字段。我的模型与关系如下:

class Company < ActiveRecord::Base
has_many: locations, dependent: :destroy
has_many :addresses, through: :locations
has_many :contacts

accepts_nested_attributes_for :locations, :addresses, :contacts
end

class Address < ActiveRecord::Base
has_many :locations
has_many :companies, through: :locations

accepts_nested_attributes_for :locations, :companies
end


class Contact < ActiveRecord::Base
belongs_to :company
accepts_nested_attributes_for :company
end

class Location < ActiveRecord::Base
belongs_to :company
belongs_to :address
accepts_nested_attributes_for :company, :address
end

我的控制器目前看起来像这样:

class CompaniesController < ApplicationController
def new
@company = Company.new
@location = @company.locations.build
@address = @company.addresses.build
@contact = @company.contacts.build
end

def create
@company = Company.new(params[:company])
if @company.save
  #handle a successful save
  flash[:success] = "Company Created Successfully"
  redirect_to @company
    else 
      render 'new'
    end
  end
end

提交表单时出现此错误:无法批量分配受保护的属性:addresses_attributes,locations_attributes,contacts_attributes

我尝试将控制器中的create方法更改为以下内容:

def create
@company = Company.new(params[:company_name])
@company.addresses.build(params[:address]) 
@company.locations.build(params[:location])
@company.contacts.build(params[:contact]) 

if @company.save
  #handle a successful save
  flash[:success] = "Company Created Successfully"
      redirect_to @company
    else 
      render 'new'
    end
  end

此create方法的结果是服务器日志,其中显示:

> Processing by CompaniesController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"SVDIk5IzY7foo9DULhzY+RWgh/HAA9NqRp6FafWwFDg=", "company"=>{"company_name"=>"New Co", "
addresses_attributes"=>{"0"=>{"address_line_1"=>"231 Main", "address_line_2"=>"", "address_line_3"=>"", "city"=>"Dallas", "state"=>"AL", 
"country"=>"USA", "zipcode"=>"74343"}}, "locations_attributes"=>{"0"=>{"location_type"=>"11", "location_name"=>"DFW"}}, "contacts_attribu
tes"=>{"0"=>{"first_name"=>"Joe", "last_name"=>"User", "title"=>"CEO"}}}, "commit"=>"Save Info"}
  SQL (24.9ms)  BEGIN TRANSACTION
  Address Exists (27.6ms)  EXEC sp_executesql N'SELECT TOP (1) 1 AS one FROM [addresses] WHERE ([addresses].[address_line_1] IS NULL AND 
[addresses].[address_line_2] IS NULL AND [addresses].[address_line_3] IS NULL AND [addresses].[address_line_4] IS NULL AND [addresses].[a
ddress_line_5] IS NULL AND [addresses].[city] IS NULL AND [addresses].[state] IS NULL AND [addresses].[county] IS NULL AND [addresses].[c
ountry] IS NULL AND [addresses].[zipcode] IS NULL)'
  SQL (50.8ms)  IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION
  CACHE (0.0ms)  SELECT @@TRANCOUNT
  Rendered companies/new.html.erb within layouts/application (9.4ms)
  Rendered layouts/_shim.html.erb (0.0ms)
  User Load (32.1ms)  EXEC sp_executesql N'SELECT TOP (1) [users].* FROM [users] WHERE [users].[remember_token] = N''TZlKZ6Sx06p3mMS9kUJY
GA'''

请注意,尽管填充了address_attributes,但查询的params [:address]对于所有字段都为null。 (*注意我在地址模型中有一个验证器,以确保每个地址都是唯一的。地址表中目前没有记录)。

如何在提交时为每个模型正确构建和存储记录?谢谢!

更新:我没有在公司模型attr_accessible块中列出的addresses_attributes,locations_attributes和contacts_attributes。添加这些属性似乎解决了从控制器和

中的表单中加载子属性的问题
@company = Company.new(params[:company])

现在填充地址,位置和联系人,但是当我打电话

if @company.save

事务仍然使用以下服务器日志回滚

  

于2013-12-11 11:12:03 -0600开始发布“/ companies”for 127.0.0.1   SQL(25.3ms)BEGIN TRANSACTION   SQL(50.4ms)IF @@ TRANCOUNT&gt; 0 ROLLBACK TRANSACTION   CACHE(0.0ms)SELECT @@ TRANCOUNT

不确定为什么事务似乎在保存时回滚。我正在使用sql server 2008和tinytds,如果这有帮助。

2 个答案:

答案 0 :(得分:0)

嵌套属性允许您通过父级保存关联记录的属性。这里父母是公司记录,嵌套是位置,地址和联系人。只有在公司模型中才需要accepts_nested_attributes_for。在嵌套的地址,联系人和位置模型中不需要accepted_nested_attributes,而不是父项。

答案 1 :(得分:0)

好的,我的代码存在多个问题。我已经彻底淘汰了所有这些并希望发布最终的工作解决方案,以防其他人通过关联和嵌套表单处理has_many。

我的最终模型看起来像这样(使用Rails 3.2.13):

Company.rb

class Company < ActiveRecord::Base
attr_accessible :locations_attributes, :contacts_attributes, :company_name

has_many :locations, dependent: :destroy
has_many :addresses, through: :locations
has_many :contacts

accepts_nested_attributes_for :locations, :reject_if => :all_blank, :allow_destroy => true
accepts_nested_attributes_for :addresses
accepts_nested_attributes_for :contacts

end

Location.rb

class Location < ActiveRecord::Base
attr_accessible  :address_attributes, :address, :created_by, :is_active, :location_name, :location_type, :region_id, :updated_by, :website


belongs_to :company
belongs_to :address

accepts_nested_attributes_for :address, :reject_if => :all_blank

end

Address.rb

class Address < ActiveRecord::Base
attr_accessible  :address_line_1, :address_line_2, :address_line_3, :address_line_4, :address_line_5, :city, :country, :county, :created_by, :province, :state, :updated_by, :zipcode

 has_many :locations, dependent: :destroy
 has_many :companies, through: :location

accepts_nested_attributes_for :locations

end

companies_controller.rb

    class CompaniesController < ApplicationController

    def new
    @company = Company.new
    @location= @company.locations.build
    @address = @company.addresses.build
    @contact = @company.contacts.build

    end

     def show
    @company = Company.find(params[:id])
  end


  def create
    @company = Company.new(params[:company])

    if @company.save
      #handle a successful save
      flash[:success] = "Company Created Successfully"
      redirect_to @company
    else 
      render 'new'
    end 


    end

    end

new.html.erb

<% provide(:title, 'Create Company')%>
<h1>Create Company</h1>

<div class="container">

<%= form_for(@company) do |f| %>


  <% if @company.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@company.errors.count, "error") %> prohibited this company from being saved:</h2>

      <ul>
      <% @company.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

<div class="row">   
    <%= f.label :company_name, "Company Name" %>
    <%= f.text_field :company_name %>
<hr>    
</div>

<div class="row">
<div class="col-md-6"><h3>Primary Location</h3><hr></div>
<div class="col-md-6"><h3>Location Information</h3><hr></div>   

</div>


    <%= f.fields_for(:locations) do |lf| %>
    <%= lf.fields_for(:address_attributes) do |af| %>
    <%= f.fields_for(:contacts) do |cf| %>  
<div class="row" >  
    <div class="col-md-6"><%= af.label "Address 1"%><%= af.text_field :address_line_1 %></div>
    <div class="col-md-6"><%= lf.label "Location Type" %> <%= lf.select(:location_type, options_for_select([["Headquarters",11], ["Office", 12]])) %></div>
</div>

<div class="row" >  
    <div class="col-md-6"><%= af.label "Address 2"%><%= af.text_field :address_line_2 %></div>
    <div class="col-md-6"><%= lf.label "Location Name" %> <%= lf.text_field :location_name %></div> 
</div>  


<div class="row" >  
    <div class="col-md-6"><%= af.label "Address 3"%><%= af.text_field :address_line_3 %></div>
    <div class="col-md-6"><h3>Other Location Information</h3><hr></div> 
</div>  
<div class="row" >  
    <div class="col-md-6"><%= af.label "City"%><%= af.text_field :city %></div>
    <div class="col-md-3"><button type="button" class="btn btn-primary btn-small btn-block">Services Offered</button></div> 
</div>  

<div class="row" >  
    <div class="col-md-6"><%= af.label "State"%><%= af.select(:state, options_for_select([["Alabama","AL"], ["Alaska","AK"]])) %></div>
    <div class="col-md-3"><button type="button" class="btn btn-primary btn-small btn-block">Materials Accepted</button></div>   
</div>  

<div class="row" >  
    <div class="col-md-6"><%= af.label "Country"%><%= af.select(:country, options_for_select([["United States","USA"], ["United Kingdom","UK"]])) %></div>
    <div class="col-md-3"><button type="button" class="btn btn-primary btn-small btn-block">Location Certifications</button></div>  
</div>  

<div class="row" >  
    <div class="col-md-6"><%= af.label "Zipcode"%><%= af.text_field :zipcode %></div>   
</div>  

<div class="row" >
<div class="col-md-12"><h3>Tell us about you</h3></div>
<hr>
</div>
<div class="row">
    <div class="col-md-6"><%= cf.label "First Name"%><%= cf.text_field :first_name %></div>
</div>
<div class="row">
    <div class="col-md-6"><%= cf.label "Last Name"%><%= cf.text_field :last_name %></div>
</div>      
<div class="row">
    <div class="col-md-6"><%= cf.label "Title"%><%= cf.text_field :title %></div>
</div>      
<div class="row">
    <div class="col-md-2"><%= f.submit "Save Info", class: "btn btn-small btn-primary" %></div>
</div>   
    <% end %>
    <% end %>
    <% end%>


<% end %>

需要注意的是accepts_nested_attributes_for语句如何链接关联,需要在每个模型attr_accessible语句中添加适当的model_attributes,以及表单如何使用:addresses_attributes在locations_form(lf)中嵌套地址格式(af) 。

此外,我必须从Location.rb中删除外键(company_id,address_id)验证,因为它们导致事务在创建地址或公司记录之前回滚(创建位置的先决条件)