在具有自己属性的连接表上使用accepts_nested_attributes_for - 重复行

时间:2010-11-25 16:20:41

标签: ruby-on-rails model nested-attributes

我有以下三个模型(Rails 2.3.8)

    class Outbreak < ActiveRecord::Base
        has_many :incidents, :dependent => :destroy
        has_many :locations, :through => :incidents

        accepts_nested_attributes_for :incidents, :allow_destroy => true
        accepts_nested_attributes_for :locations, :allow_destroy => true, :reject_if => proc { |attrs| attrs.all? { |k, v| v.blank? } }

    end

   class Incident < ActiveRecord::Base
        belongs_to :outbreak
        belongs_to :location
   end


   class Location < ActiveRecord::Base
 has_many :incidents
 has_many :outbreaks, :through => :incidents

     accepts_nested_attributes_for :incidents, :allow_destroy => true

   end

表单中的参数似乎没问题

“爆发”=&GT; { “locations_attributes”=&gt; {“0”=&gt; {“lon”=&gt;“ - 1.39”,“placename”=&gt;“wetwe”,“hpu_id”=&gt;“15”,“邮政编码”=&gt; ;“so1 1aa”,“region_id”=&gt;“10”,“address_1”=&gt;“”,“town”=&gt;“Bargate”,“address_2”=&gt;“”,“address_3”=&gt; “”,“lat”=&gt;“50.89”}},“incidents_attributes”=&gt; {“0”=&gt; {“subtype_id”=&gt;“7”,“category_id”=&gt;“1”,“ detail“=&gt;”“,”subcategory_id“=&gt;”2“}} }

但是,当保存爆发时,会在“事件”表(连接表)中创建3行,在“爆发”和“位置”表中创建一行。

事件表中的行未完全填充参数,如下所示:

id outbreak_id location_id category_id subcategory_id subtype_id detail  created_at    updated_at 

 57 23   NULL     1       2           7                          2010-11-25 14:45:18.385905  2010-11-25 14:45:18.385905 
 58 23   27         NULL       NULL        NULL    NULL           2010-11-25 14:45:18.39828  2010-11-25 14:45:18.39828 
 59 23   27         NULL         NULL     NULL      NULL           2010-11-25 14:45:18.403051  2010-11-25 14:45:18.403051 

这必须归因于参数的格式或多个accepts_nested_attributes_for方法 - 如何在Incidents表中输入一行并包含所有参数信息?

2 个答案:

答案 0 :(得分:3)

本周第二次到目前为止,我已经回答了我自己的问题^^这将教会我在放弃并在网上发帖寻求帮助之前付出更多努力,

在查看我原来的问题之后,我没有提供足够的信息来正确回答 - 问题(除了模型的设置)是由爆发控制器新方法中的爆发构造函数,

Original Outbreaks_controller

def new

    @outbreak = Outbreak.new
    @outbreak.risks.build
    //links locations directly to Outbreak instead of through Incidents
    @outbreak.locations.build
    @outbreak.incidents.build

    respond_to do |format|
      format.html # new.html.erb
      format.xml  { render :xml => @outbreak }
    end
end

修订了Outbreaks_controller

def new

    @outbreak = Outbreak.new
    @outbreak.risks.build
    //builds Incidents then a Location through that incident
    @outbreak.incidents.build.build_location

    respond_to do |format|
      format.html # new.html.erb
      format.xml  { render :xml => @outbreak }
    end
end

对三个模型的更改

    class Outbreak < ActiveRecord::Base
        has_many :incidents, :dependent => :destroy
        has_many :locations, :through => :incidents

        accepts_nested_attributes_for :incidents, :allow_destroy => true


    end

   class Incident < ActiveRecord::Base
        belongs_to :outbreak
        belongs_to :location

        accepts_nested_attributes_for :location, :allow_destroy => true
   end


   class Location < ActiveRecord::Base
       has_many :incidents
       has_many :outbreaks, :through => :incidents

   end

这似乎工作正常 - 还发布了创建动作和主要表单

答案 1 :(得分:1)

创建操作只需要为以下内容提供的嵌套参数:爆发(模型可以完成工作)。

def create

    @outbreak = Outbreak.new(params[:outbreak])
    @outbreak.user_id = current_user.id

        respond_to do |format|
     if @outbreak.save
        flash[:notice] = 'Outbreak was successfully created.'
        format.html { redirect_to(@outbreak) }
        format.xml  { render :xml => @outbreak, :status => :created, :location => @outbreak }
      else
        format.html { render :action => "new" }
        format.xml  { render :xml => @outbreak.errors, :status => :unprocessable_entity }
      end
    end
end

爆发形式很长,所以我把它简化为所提到的两个部分(虽然这里可能有更多属性和字段,但需要了解一下)。

嵌套字段的HTML元素id的示例可以在Javascript observe_field帮助器的底部找到。我在nested上更新nested_attributes_for partials的帖子可能也很有用AJAX update of accepts_nested_attributes_for

    <% form_for(@outbreak, :html => {:multipart => true}) do |form| %>
    <%= form.error_messages %>
    <div id="tabs">
        <ul>

            <li ><a href="#tabs_b">Outbreak</a></li>
            <li ><a href="#tabs_c">Location</a></li>

        </ul>   


            <div id="tabs_b">
                <fieldset id="b" class="form_div">
                    <legend>Outbreak</legend>

                    <fieldset>
                        <legend>References</legend>
                      <div class="left_form">
                        <%= form.label :user_reference %>
                      </div>
                      <div class="right_form">
                        <%= form.text_field :user_reference %>
                      </div>
                      <div style="clear:both;"></div>

                    </fieldset>

                </fieldset>
            </div>
            <div id="tabs_c">
                <fieldset id="c" class="form_div" >

                    <legend>Location</legend>
                      <div id="location_error"></div>
                            <fieldset>
                            <legend>Setting</legend>
                <% form.fields_for :incidents do |incident_form| %>

                                  <div class="left_form">
                                    <%= incident_form.label :category_id %>
                                  </div>

                                  <div class="right_form">
                                    <div id="incident_category_select">
                                    <%= render :partial => 'category_select', :locals => {:categories => @categories, :incident_form => incident_form} %>
                                    </div>
                                  </div>
                                  <div style="clear:both;"></div>

                                  <div class="left_form">
                                    <%= incident_form.label :subcategory_id %>
                                  </div>
                                  <div class="right_form">
                                    <div id="incident_subcategory_select">
                                    <%= render :partial => 'subcategory_select', :locals => { :subcategories => @subcategories, :incident_form => incident_form } %>
                                    </div>
                                  </div>
                                  <div style="clear:both;"></div>
                                  <div class="left_form">
                                    <%= incident_form.label :subtype_id %>
                                  </div>
                                  <div class="right_form">
                                    <div id="incident_subtype_select">
                                    <%= render :partial => 'subtype_select',  :locals => { :subtypes => @subtypes, :incident_form => incident_form } %>
                                    </div>
                                  </div>
                                  <div style="clear:both;"></div>
                                  <div id="cuisine_div">
                                    <% if @outbreak.outbreak_type == "FOODBORNE" %>
                                        <div class="left_form">
                                            <%= label :incident, :cuisine_id %>
                                        </div>
                                        <div class="right_form">
                                            <% cuisine_select = (@incident != nil ? @incident.cuisine_id.to_i : '') %>
                                            <%= incident_form.select( :cuisine_id, "<option value='' >Please select</option>" + options_from_collection_for_select(@cuisines, :id, :name, cuisine_select)) %>
                                        </div>
                                    <% end %>

                                  </div>
                                  <div style="clear:both;"></div>
                                  <div class="left_form">
                                    <%= incident_form.label :detail %>
                                  </div>
                                  <div class="right_form">
                                    <%= incident_form.text_field :detail %>
                                  </div>


                        </fieldset>
                        <fieldset>
                            <legend>Details</legend>
                            <%  incident_form.fields_for :location do |location_form| %>
                                  <div style="clear:both;"></div>
                                   <div class="left_form">
                                    <%= location_form.label :placename %>
                                  </div>
                                  <div class="right_form">
                                    <%= location_form.text_field :placename %>
                                  </div> 
                                  <div style="clear:both;"></div>
                                  <div class="left_form">
                                    <%= location_form.label :address_1 %>
                                  </div>
                                  <div class="right_form">
                                    <%= location_form.text_field :address_1 %>
                                  </div>
                                  <div style="clear:both;"></div>
                                  <div class="left_form">
                                    <%= location_form.label :address_2 %>
                                  </div>
                                  <div class="right_form">
                                    <%= location_form.text_field :address_2 %>
                                  </div>
                                  <div style="clear:both;"></div>
                                  <div class="left_form">
                                    <%= location_form.label :address_3 %>
                                  </div>
                                  <div class="right_form">
                                    <%= location_form.text_field :address_3 %>
                                  </div> 
                                  <div style="clear:both;"></div>
                                  <div class="left_form">
                                    <%= location_form.label :town %>
                                  </div>
                                  <div class="right_form">
                                    <%= location_form.text_field :town %>
                                  </div>
                                  <div style="clear:both;"></div>
                                  <div class="left_form">
                                    <%= location_form.label :postcode %>
                                  </div>
                                  <div class="right_form">
                                    <%= location_form.text_field :postcode %>
                                  </div>
                                  <div style="clear:both;"></div>        
                                  <div class="left_form">
                                    <%= location_form.label :region_id %>
                                  </div>
                                  <div class="right_form" >
                                        <% region_select = (@location != nil ? @location.region_id.to_i : '') %>
                                    <%= location_form.select(:region_id, "<option value=''>Select a region</option>" + options_from_collection_for_select(@regions, :id, :name, region_select)) %>
                                  </div>
                                  <div style="clear:both;"></div>
                                  <div class="left_form">
                                    <%= location_form.label :hpu_id %>
                                  </div>
                                  <div class="right_form" >
                                    <% hpu_select = (@location != nil ? @location.hpu_id.to_i : '') %>
                                    <%= location_form.select(:hpu_id, "<option value=''>Select a HPU</option>" + options_from_collection_for_select(@hpus, :id, :name, hpu_select)) %>
                                  </div>
                                  <div style="clear:both;"></div>

                                <%= location_form.hidden_field :lon, :value => '' %>
                                <%= location_form.hidden_field :lat, :value => '' %>
                                <%= hidden_field_tag :postcode_error, :value => '0' %>
                                <% end %>
                          </fieldset>


                    <% end %>       

                </fieldset>

            </div>


    </div>
    <% end %>

    <div style="clear: both; margin: 10px;"></div>
    <%= observe_field(:outbreak_incidents_attributes_0_location_attributes_postcode, 
              :url => { :controller => :locations, :action => :find_lonlat },
              :on => "onchange",
              :loading => "Element.show('loader')",
              :success => "Element.hide('loader')",
              :with => "'postcode=' + encodeURIComponent($('outbreak_incidents_attributes_0_location_attributes_postcode').value)" ) %>