Usando accepts_nested_attributes_for en una tabla de unión con sus propios atributos - filas duplicadas
-
28-09-2019 - |
Pregunta
Tengo los tres modelos siguientes (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
Los parámetros de la forma parecen estar bien
"brote" => { "Locations_attributes" => { "0" => { "lon" => "- 1.39", "placename" => "wetwe", "hpu_id" => "15", "código postal" => "1 bis bis SO1", "REGION_ID" => "10", "address_1" => "", "ciudad" => "Bargate", "address_2" => "", "address_3" => "", "lat" => "50.89" }}, "incidents_attributes" => { "0" => { "subtype_id" => "7", "category_id" => "1", "detalle" => "", "subcategory_id" => "2"} } }
Sin embargo, cuando el brote se guarda 3 filas se crean en la tabla Incidentes (la tabla de unión) y una sola fila en el brote y tablas Ubicación.
Las filas de la tabla incidentes no se rellenan completamente de los params como sigue:
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
Esto debe ser debido a la ya sea el formato de los parámetros o los múltiples métodos accepts_nested_attributes_for -? ¿Cómo tengo una sola fila que se está introducida en la tabla de incidentes con toda la información de parámetros
Solución
La segunda vez en lo que va de esta semana he respondido a mi propia pregunta ^^ que me enseñará a poner más esfuerzo en antes de abandonar y publicación en la red en busca de ayuda,
Aún después de mirar en mi pregunta original no he incluido información suficiente para responder de forma adecuada - el problema (aparte de la puesta en marcha de los modelos) se redujo al constructor del brote en el controlador brote nuevo método,
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
Revisado 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
Los cambios en los tres modelos
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
Esto parece bien el trabajo - también publicó la acción y crear forma principal
Otros consejos
La acción de crear sólo necesita los parametros anidados previstos: brote (los modelos de hacer el trabajo)
.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
La forma de brote es bastante largo corte por lo que he abajo a las dos secciones mencionadas (aunque es probable que haya más atributos y campos aquí que es necesario tener una idea).
Un ejemplo de la Identificación del elemento HTML de los campos anidados se puede encontrar en la parte inferior en el ayudante observe_field Javascript. Un post que hice sobre las actualizaciones parciales de AJAX nested_attributes_for también podría ser útil AJAX de 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)" ) %>