Reputation: 6466
Using Nested Model Form (revised) Railscast to try to create a page with nested models which can be updated at once. Wikis have many topics, which have many notes.
The problem is, a) The pre-existing data isn't pre-populating the fields the way it would if this were performing correctly, and b) hitting update doesn't throw any errors (and in fact, it shows the success banner), but it doesn't do anything, either.
Here's my code:
wiki, topic, note.rb (combined for reading, not in actuality):
class Wiki < ActiveRecord::Base
attr_accessible :topics_attributes
has_many :topics
accepts_nested_attributes_for :topics, :allow_destroy => true
end
class Topic < ActiveRecord::Base
attr_accessible :name, :notes_attributes, :wiki_id
has_many :notes
belongs_to :wiki
accepts_nested_attributes_for :notes, :allow_destroy => true
end
class Note < ActiveRecord::Base
attr_accessible :name, :info, :topic_id
belongs_to :topic
end
wikis_controller.rb
I'm doing the wiki.first thing because I only want one wiki. The model exists so I can update its dependent models through a single object. I will never have more than that one wiki.
class WikisController < ApplicationController
before_filter :authorize
def show
@wiki = Wiki.first
end
def edit
@wiki = Wiki.first
end
def update
@wiki = Wiki.first
respond_to do |format|
if @wiki.update_attributes(params[:wiki])
format.html { redirect_to wikis_path, notice: 'Wiki was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: @wiki.errors, status: :unprocessable_entity }
end
end
end
end
routes.rb # I only want one "wiki", and I want the route for it to be /wikis get 'wikis', to: 'wikis#show', as: 'home' resources: wikis
wikis/edit.html.haml
.row.container.wiki
%h1{ :style => 'margin-bottom: 40px'} Update the Wiki
= form_for @wiki do |w|
= w.fields_for :topics do |builder|
= render "topic_fields", f: builder
= link_to_add_fields "add topic", w, :topics
%br
= w.submit "Update", class: "btn"
wikis/_topic_fields.html.haml
.field_inset
= f.text_field :name
= f.hidden_field :_destroy
= link_to "remove", '#', class: "remove_fields"
= f.fields_for :notes do |builder|
= render 'note_fields', f: builder
wikis/_notes_fields.html.haml
.field_inset
.add_inset
= f.fields_for :notes do |builder|
.remove
= builder.text_field :name
= builder.text_field :info, class: "no_margin"
= link_to "remove", '#', class: "remove_fields"
The classes are mostly for styling, with the exception of remove_fields, which calls some javascript. Any idea what I'm doing wrong here? I'm pretty stumped by this.
NEW ERROR
After fixing my update action in wikis_controller (which used to reference params[:post], by mistake), I now get this error when I try to submit an update. Still, the fields are NOT prepopulated:
Can't mass-assign protected attributes: notes
I would have thought that the :topics_attributes and :notes_attributes would take care of this??
ALSO, it's worth noting that while the Notes fields aren't prepopulated, the Topics field is.
EDIT -- params hash from mass-assign error below
It looks like the params are getting passed correctly
{"utf8"=>"✓",
"_method"=>"put",
"authenticity_token"=>"3/4k8eEa4/PfeNI9cSj7zqCm9+scWBS3gwEacmc/hd0=",
"wiki"=>{"topics_attributes"=>{"0"=>{"name"=>"Pre-existing Topic Name",
"_destroy"=>"false",
"notes_attributes"=>{"0"=>{"notes"=>{"name"=>"New Notes Name",
"info"=>"New Notes Info"},
"id"=>"1"}},
"id"=>"1"}}},
"commit"=>"Update",
"id"=>"1"}
Upvotes: 0
Views: 238
Reputation: 6921
You have one to many fields_for call in your view. Basic rule of thumb is to have one fields_for call for each accepts_nested_attributes_for call.
change wikis/_notes_fields.html.haml from:
.field_inset
.add_inset
= f.fields_for :notes do |builder|
.remove
= builder.text_field :name
= builder.text_field :info, class: "no_margin"
= link_to "remove", '#', class: "remove_fields"
to:
.field_inset
.add_inset
.remove
= f.text_field :name
= f.text_field :info, class: "no_margin"
= link_to "remove", '#', class: "remove_fields"
I am not a haml guy so I am not 100% sure is this code valid but basic idea is to remove fields_for call.
Your notes_attributes hash looks like this:
"notes_attributes"=>{"0"=>{"notes"=>{"name"=>"New Notes Name",
"info"=>"New Notes Info"}, "id"=>"1"}}
And it should:
"notes_attributes"=>{"notes"=>{"name"=>"New Notes Name",
"info"=>"New Notes Info"}}
Upvotes: 1