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

发布于 2024-10-04 06:01:12 字数 1817 浏览 5 评论 0原文

我有以下三个模型(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

表单中的参数似乎没问题

"outbreak"=>{ "locations_attributes"=>{"0"=>{"lon"=>"-1.39", "地名"=>"wetwe", "hpu_id"=>"15", "邮政编码"=> ;“so1 1aa”,“region_id”=>“10”,“address_1”=>“”,“town”=>“Bargate”,“address_2”=>“”,“address_3”=> "", "lat"=>"50.89"}},"incidents_attributes"=>{"0"=>{"subtype_id"=>"7", "category_id"=>"1", "详细信息"=>"","subcategory_id"=>"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 方法造成的 - 我如何在事件表中仅输入一行并包含所有内容的参数信息?

I have the following three models (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

The parameters from the form seem to be ok

"outbreak"=>{
"locations_attributes"=>{"0"=>{"lon"=>"-1.39", "placename"=>"wetwe", "hpu_id"=>"15", "postcode"=>"so1 1aa", "region_id"=>"10", "address_1"=>"", "town"=>"Bargate", "address_2"=>"", "address_3"=>"", "lat"=>"50.89"}},"incidents_attributes"=>{"0"=>{"subtype_id"=>"7", "category_id"=>"1", "detail"=>"", "subcategory_id"=>"2"}}
}

But when the Outbreak is saved 3 rows are created in the Incidents table (the join table) and a single row in the Outbreak and Location tables.

The rows in the Incidents table are not fully populated from the params as follows:

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 

This must be due to the either the format of the parameters or the multiple accepts_nested_attributes_for methods - how do I have just a single row being entered in the Incidents table with all of the parameters information?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

东风软 2024-10-11 06:01:13

创建操作只需要为 :outbreak 提供的嵌套参数(模型完成工作)。

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 的示例可以在 Javascriptobserve_field 帮助器的底部找到。我发表的关于 Nested_attributes_for 部分的 AJAX 更新的帖子也可能有用 AJAX 更新的 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)" ) %>

The create action only needs the nested params provided for :outbreak (the models do the work).

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

The outbreak form is pretty long so I've cut it down to the two sections mentioned (although there's probably more attributes and fields here than is need to get an idea).

An example of the HTML element id for the nested fields can be found at the bottom in the Javascript observe_field helper. A post I made on AJAX updates of nested_attributes_for partials might also be useful 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)" ) %>
一人独醉 2024-10-11 06:01:12

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

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

原始 O​​utbreaks_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

这似乎工作正常 - 还发布了创建操作和主要形式

Second time so far this week I've answered my own question ^^ that'll teach me to put more effort in before giving up and posting on the net for help,

Still after looking at my original question I didn't include enough information to answer it properly - the issue (apart from the set up of the models) was down to the Outbreak constructor in the Outbreak controller new method,

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

Revised 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

Changes to the three models

    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

This seems to work ok - also posted the create action and main form

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文