Thanks to help on stackoverflow I got my create nested models form working the other day but I can't for the life of me get the corresponding update form to work. I have read a lot and tried out as many solutions as I can find.
The form looks fine but the nested attributes of Manufacturer and Scale, which are select via drop down, don't have their current values. All non nested elements of the form work fine.
Whatever changes you make to the two nested drop downs, pressing save changes creates NEW lines in the corresponding tables and doesn't alter the existing.
Ultimately what I want is for the attributes to be editable and then i'll have an "add manufacturer" and "add scale" button or link for miniatures that need multiple listings.
Here are my form fields where I've tried and failed to pass a hidden field.
Form
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :material %>
<%= f.select 'material', options_from_collection_for_select(Miniature.select("DISTINCT material"), :material, 'material', @miniature.material) %>
<%= f.fields_for :sizes do |size_fields| %>
<%= size_fields.label :scale_id, "Scale".pluralize %>
<%= hidden_field "Miniature Scale", @miniature.sizes %>
<%= size_fields.select :scale_id, options_from_collection_for_select(Scale.all, :id, :name) %>
<% end %>
<%= f.fields_for :productions do |production_fields| %>
<%= production_fields.label :manufacturer_id, "Manufacturer".pluralize %>
<%= hidden_field "Miniature Manufacturer", @miniature.productions %>
<%= production_fields.select :manufacturer_id, options_from_collection_for_select(Manufacturer.all, :id, :name, @miniature.manufacturers) %>
<% end %>
<%= f.label :release_date %>
<%= f.date_select :release_date, :start_year => Date.current.year, :end_year => 1970, :include_blank => true %>
Here is the miniatures controller where I'm pretty sure I've filled the 'def update' with too much/the wrong stuff.
Miniatures Controller
class MiniaturesController < ApplicationController
before_action :signed_in_user, only: [:new, :create, :edit, :update]
before_action :admin_user, only: :destroy
def show
@miniature = Miniature.find(params[:id])
end
def new
@miniature = Miniature.new
@miniature.productions.build
@miniature.sizes.build
end
def create
@miniature = Miniature.new(miniature_params)
@production = @miniature.productions.build
@size = @miniature.sizes.build
if @miniature.save
redirect_to @miniature
else
render 'new'
end
end
def edit
@miniature = Miniature.find(params[:id])
end
def update
@miniature = Miniature.find(params[:id])
@production = @miniature.productions.find(params[:id])
@size = @miniature.sizes.find(params[:id])
if @miniature.update_attributes(miniature_params)
@production = @miniature.productions.update_attributes(:manufacturer_id)
@size = @miniature.sizes.update_attributes(:scale_id)
flash[:success] = "Miniature updated"
redirect_to @miniature
else
render 'edit'
end
end
def index
@miniatures = Miniature.paginate(page: params[:page])
end
def destroy
Miniature.find(params[:id]).destroy
flash[:success] = "Miniature destroyed."
redirect_to miniatures_url
end
private
def miniature_params
params.require(:miniature).permit(:name, :release_date, :material, productions_attributes: [:manufacturer_id], sizes_attributes: [:scale_id])
end
def admin_user
redirect_to(root_url) unless current_user.admin?
end
def signed_in_user
unless signed_in?
store_location
redirect_to signin_url, notice: "Please sign in."
end
end
end
I won't attach the models as I'm pretty sure the relationships are all correct since they work fine for creating new nested models. Miniatures have_many scales and manufactures through sizes and productions.
Any help or pointers very much appreciated.
Thanks to an answer on this Q I've solved it. What I had already was fine for CREATING but didn't work for UPDATES because I hadn't whitelisted the JOIN model ids in the 'miniature_params, so they couldn't retrieve the existing info.
Now I have productions_attributes: [:id, :manufacturer_id]
instead of just productions_attributes: [:manufacturer_id]
as below
def miniature_params
params.require(:miniature).permit(:name, :release_date, :material, productions_attributes: [:id, :manufacturer_id], sizes_attributes: [:id, :scale_id])
end
I can also strip ALL of the references to the nested models out of my miniatures controller update method as it 'just works'.
def update
@miniature = Miniature.find(params[:id])
if @miniature.update_attributes(miniature_params)
flash[:success] = "Miniature updated"
redirect_to @miniature
else
render 'edit'
end
end
Hope this is useful to someone in future.