Forms with nested models - ruby-on-rails

I'm trying to make a site that has a form with one model ("Requests") nested in another ("Orders"). Specifically, I'd like the orders/new page to have a form composed of requests that a user can fill and then submit, so they're all associated with the order that is created.
On someone here's suggestion, I looked into the Railscast on the topic (#196), and I've run into a problem I can't figure out, so I figured I'd ask. The problem is, I'm following his directions, but the request forms just aren't showing up. The form fields associated with the Order model ARE showing up, however, and I'm really confused, cause I basically copied the Railscast verbatim. Thoughts?
Here's my form code. It's specifically the part in the fields_for tag that isn't showing up on my site:
<%= form_for(#order) do |f| %>
<% if #order.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#order.errors.count, "error") %> prohibited this order from being saved:</h2>
<ul>
<% #order.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :user_name %><br />
<%= f.text_field :user_name %>
</p>
<p>
<%= f.label :user_email %><br />
<%= f.text_field :user_email %>
</p>
<% f.fields_for :requests do |builder| %>
<p>
<%= builder.label :item, "Item" %><br />
<%= builder.text_field :item %>
</p>
<p>
<%= builder.label :lbs, "LBs" %> <br />
<%= builder.text_field :lbs %>
</p>
<% end %>
<p><%= f.submit "Submit Order" %></p>
<% end %>
And here's my code for the two models, Order first:
class Order < ActiveRecord::Base
attr_accessible :order_status, :price_estimate, :price_final, :user_email, :user_name, :user_phone
has_many :requests, :dependent => :destroy
accepts_nested_attributes_for :requests
end
class Request < ActiveRecord::Base
attr_accessible :item, :lbs, :notes, :order_id, :status
belongs_to :order
end
Finally, in case it's relevant, the bit I added, per the cast's suggestions, to my Order controller create action:
def new
#order = Order.new
3.times { #order.requests.build }
respond_to do |format| # this part I copied from a tutorial. But when I commented it out, it didn't change anything, so I don't think this is the problem
format.html # new.html.erb
format.json { render json: #order }
end
end
Help? I'm super confused by this. I'm guessing the problem might have something to do with the "builder," but I'm new to this, and an hour or so of fiddling around hasn't yielded much.
Really appreciate any help. (Also, as a post-script. I'm giving up and going to bed, to look at this tomorrow. Sorry if I don't follow up on any questions til then).
Thanks!

I see two mistakes here.
In the view, replace:
<% f.fields_for :requests do |builder| %>
With:
<%= f.fields_for :requests do |builder| %>
In your order modeil, replace:
attr_accessible :order_status, :price_estimate, :price_final, :user_email, :user_name, :user_phone
with:
attr_accessible :order_status, :price_estimate, :price_final, :user_email, :user_name, :user_phone, :requests_attributes

Related

how to update nested attribute in form

I wanna update the nested attribute but failed, for example there is a Article, and a book has many comments. when I find the comment I have written has some mistakes, so I wanna modify it.
here is my code.
In code_snippet.rb:
class CodeSnippet < ApplicationRecord
has_many :annotations, dependent: :destroy
accepts_nested_attributes_for :annotations ,update_only: true ,reject_if: :all_blank, allow_destroy: true
end
In annotation.rb:
class Annotation < ApplicationRecord
belongs_to :code_snippet
end
In code_snippet_controller.rb:
def edit
#code_snippet = CodeSnippet.find(params[:id])
end
def update
#code_snippet = CodeSnippet.find(params[:id])
if #code_snippet.update(code_snippet_params)
redirect_to #code_snippet
else
render 'edit'
end
end
private
def code_snippet_params
params.require(:code_snippet).permit(:snippet)
end
In annotation.rb:
def edit
#code_snippet = CodeSnippet.find(params[:code_snippet_id])
#annotation = #code_snippet.annotations.find(params[:id])
end
def update
#code_snippet = CodeSnippet.find(params[:id])
#annotation = #code_snippet.annotations.find(params[:id])
if #annotation.update(annotation_params)
redirect_to #code_snippet
else
render 'edit'
end
end
In 'views/code_snippets/show.html.rb'
<div>
<h2>Annotations</h2>
<%= render #code_snippet.annotations %>
</div>
In 'views/annotations/_annotation.html.erb'
<p>
<strong>User:</strong>
<%= annotation.user %>
</p>
<p>
<strong>Line:</strong>
<%= annotation.line %>
</p>
<p>
<strong>Body:</strong>
<%= annotation.body %>
</p>
<p>
<%= link_to "Edit", edit_code_snippet_annotation_path(annotation.code_snippet,annotation) ,controller: 'annotation'%>
</p>
In 'views/annotations/edit.html.erb':
<%= form_for(#code_snippet) do |f| %>
<%= f.fields_for :annotation,method: :patch do |builder| %>
<p>
<%= builder.label :user %><br>
<%= builder.text_field :user %>
</p>
<p>
<%= builder.label :line %><br>
<%= builder.text_field :line %>
</p>
<p>
<%= builder.label :body %><br>
<%= builder.text_area :body %>
</p>
<p>
<%= builder.submit %>
</p>
<% end %>
<% end %>
what I wanna update the annotation without change the codesnippets. what should I do to change my code.
So.... There's a lot going on here so I'm going to start by suggesting a careful look at the docs
Firstly, let's look at your form:
CodeSnippet has_many :annotations
So your fields_for statement should be for :annotations, not :annotation. The fields for statement also should not take a method options key.
Next your code_snippets_controller:
As indicated by the docs, the parameter sent back from the nested attributes form will be under the key: annotations_attributes and will contain a hash of arrays.
You need to allow this attribute, and any attribute you wish to pass onto the annotation model with strong parameters:
params.require(:code_snippet).permit(annotations_attributes: [:some, : permitted, :params])
I believe this is all you need to get your example working. But, if you run into more troubles, I recommend a spending some quality time with a few binding.pry statements to introspect on the actual behaviour of your code.

rails 4 nested form fields_for are not displayed

I just started learning Rails 4.2. The problem is that one field in the form is not being displayed.
I have restaurant, category and a dish. While creating a dish, the category and restaurant will also be inputted via /dishes/new.
Expected behaviour: Dish, Category and Restaurant fields are displayed.
Actual behaviour: Only Dish and Category fields are displayed.
Here are my models
models/restaurant.rb
class Restaurant < ActiveRecord::Base
has_many :categories
has_many :dishes, :through => :categories
end
models/category.rb
class Category < ActiveRecord::Base
belongs_to :restaurant
has_many :dishes
end
models/dish.rb
class Dish < ActiveRecord::Base
belongs_to :category
validates :name, :price, :category, :restaurant, :presence => true
accepts_nested_attributes_for :restaurant, :category
end
dish controller
def new
# I think this is where
# I am making a mistake
#dish = Dish.new
category = #dish.build_category
restaurant = category.build_restaurant
end
def create
#dish = Dish.new(dish_params)
respond_to do |format|
if #dish.save
.... # default stuff #
end
end
end
# strong params
def dish_params
params.require(:dish).permit(:name, :description, :price, restaurant_attributes: [:name], category_attributes: [:name])
end
Dishes views/dishes/_form.html.erb
<%= form_for(#dish) do |f| %>
<% if #dish.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#dish.errors.count, "error") %> prohibited this dish from being saved:</h2>
<ul>
<% #dish.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :nameWoW %><br>
<%= f.text_area :name %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>
<div class="field">
<%= f.label :price %><br>
<%= f.number_field :price %>
</div>
*** The restaurant name field is not being displayed **
<%= f.fields_for :restaurant do |restaurant| %>
<div class="field">
<%= restaurant.label :Restname %><br>
<%= restaurant.text_area :name %>
</div>
<% end %>
<%= f.fields_for :category do |category| %>
<div class="field">
<%= category.label :Catname %><br>
<%= category.text_area :name %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
I have followed steps from rails guide, browsed questions on stackoverflow and read some blog posts as well but havent been able to figure out whats wrong. Some micro level mistake is blocking me :( . Anyone knows whats wrong ?
Thanks in advance.
UPDATE:
Hey I found a solution.
def new
#dish = Dish.new
#dish.build_category
#dish.category.build_restaurant
end
This works well.But this is just a part of the actual solution. I had to do lot of /dish/create controller modification as well. I think the entire solution will have to be put in blog post. Otherwise it wont make any sense. I will soon be posting and updating it here.
You can add this in your dish.rb
class Dish
delegate :restaurant, to: :category
end
Or you can do
<%= f.fields_for :restaurant, #dish.category.restaurant do |restaurant| %>
<div class="field">
<%= restaurant.label :Restname %><br>
<%= restaurant.text_area :name %>
</div>
<% end %>
I think you are missing:
class Dish
belongs_to :restaurant, through: :category
end
You have it on the other side (many) but not there. You could test this by trying to output #dish.restaurant on your form (should be empty but not nil).
def new
# I think this is where
# I am making a mistake
#dish = Dish.new
category = #dish.category.build
restaurant = category.restuarant.build
end

Nested form shows too many fields! Rails Beginner

I as Rails Beginner created an simple demo app to experiment with nested forms.
But somehow my code shows strange byproducts:
My only aim was to create new treatments for patients on the patients show page, and now
it show input fields with yet created treatments and some other crazy stuff!! What did i wrong? My steps so far:
rails new hama
cd hama
rails g scaffold Patient name:string
rails g model Treatment content:string
rake db:migrate
Patient model:
attr_accessible :name, :treatments_attributes
has_many :treatments, :dependent => :destroy
accepts_nested_attributes_for :treatments
Treatment model:
attr_accessible :content
belongs_to :patient
In patient/show:
<b>Name:</b>
<%= #patient.name %>
</p>
<p>
<b>Treatments:</b>
<%= #patient.treatments.each do |treatment| %>
<%= treatment.content %>
<% end %>
</p>
<%= form_for #patient do |f| %>
<%= f.fields_for :treatments do |ff| %>
<%= ff.text_field :content %>
<% end %>
<%= f.fields_for :treatments do |ff| %>
<%= ff.text_field :content %>
<% end %>
<%= f.submit %>
<% end %>
And in Patient controller:
def show
#patient = Patient.find(params[:id])
treatment = #patient.treatments.build
respond_to do |format|
format.html # show.html.erb
format.json { render json: #patient }
end
end
Are you talking about where it shows all the internals of your Treatment objects?
Change this:
<%= #patient.treatments.each do |treatment| %>
to this:
<% #patient.treatments.each do |treatment| %>
Using <%= %>, with the =, means to output the result of that Ruby line on to the page. Without it, it's just code that Ruby runs.
Firstly you should remove the = from this line:
<%= #patient.treatments.each do |treatment| %>
You don't want to display the output of the each. The loop contents provide the output. Just use:
<% #patient.treatments.each do |treatment| %>
All the other fields are output since that's what your code asks for. This part of your code is showing all of the same fields twice:
<%= f.fields_for :treatments do |ff| %>
<%= ff.text_field :content %>
<% end %>
<%= f.fields_for :treatments do |ff| %>
<%= ff.text_field :content %>
<% end %>
If there are two specific input fields for a treatment, then there needs to be two different attributes.

Associated object not saved with fields_for

i´m a newbie to rails and have a problem with my associated object not being saved. I think I did everything the way it should be done and I can´t figure out, why it isn´t working. So thanks in advance for everyone who can help me get a little closer to solving this problem.
These are my models :
class Examdate < ActiveRecord::Base
belongs_to :exam
attr_accessible :date, :exam_id
end
class Exam < ActiveRecord::Base
attr_accessible :title, :prof_id, :deadline
belongs_to :prof
has_many :examdates, :dependent => :destroy
accepts_nested_attributes_for :examdates
end
In my exams_controller I have this:
def new
#exam = Exam.new
3.times{#exam.examdates.build()}
end
def create
#exam = Exam.new(params[:exam])
respond_to do |format|
if #exam.save
....
Now in my view I have the semantic_fields_for method, I also tried it with normal fields_for and got the same result:
<%= semantic_form_for #exam do |f| %>
<% if #exam.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#exam.errors.count, "error") %> prohibited this post from being saved:</h2>
<ul>
<% #exam.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.inputs do%>
<%= f.input :title%>
<%= f.input :prof%>
<%= f.input :deadline, :start_year => Time.now.year, :label => "Anmeldefrist"%>
<% end %>
<%= f.semantic_fields_for :examdates do |builder|%>
<%= render "examdates_fields", :f => builder %>
<% end %>
<%= f.buttons do %>
<%= f.commit_button "Speichern"%>
<% end %>
<% end %>
In the partial is this, will later be extended
<%= f.inputs :date%>
Now I get the form with the correct three date fields and I can save the Exam itself correctly. When I look at params[:exam][:examdates_attributes] the dates are there:
{"0"=>{"date(1i)"=>"2006", "date(2i)"=>"1", "date(3i)"=>"1"},
"1"=>{"date(1i)"=>"2006", "date(2i)"=>"1", "date(3i)"=>"1"},
"2"=>{"date(1i)"=>"2006", "date(2i)"=>"1", "date(3i)"=>"1"}}
But when I put Exam.find(1).exdates in my rails Console, I get []. I really don´t have any idea what I did wrong, so every little tip is very appreciated:)
Since you are using attr_accessible in your Exam model, I think you'll have to include :examdates_attributes in that list. Otherwise, mass assignment to the nested model will not be allowed.
class Exam < ActiveRecord::Base
attr_accessible :title, :prof_id, :deadline, :examdates_attributes
...

Rails 3: nested_form, collection_select, accepts_nested_attributes_for and fields_for

Update: answered here.
There are lots of good questions and answers here and on the interweb about getting nested_form, collection_select, accepts_nested_attributes_for and fields_for to play nicely together, but I'm still stumped. Thanks in advance if you can help me.
Aim: To generate a new isbn record. An isbn can have many contributors. I am successfully using the ryanb nested_form gem to dynamically produce new contributor fields on a form, as required. One of these fields uses a collection_select drop down of all the name records in Contributor. When the new record is created, the many contributor ids need to be written to the join table (contributors_isbns).
I have got bits of this working, but only to the point where I can save a single contributor ID to the new record in the isbns table. I can't seem to get anywhere in writing any data to the join table.
I have three models. Contributors and Isbns have a many to many relationship, which I've done using has_many :through. An isbn can have many contributors, and a contributor can have many isbns. They are joined via contributors_isbn.
isbn.rb
attr_accessible :contributor_id
has_many :contributors, :through => :contributors_isbns
has_many :contributors_isbns
accepts_nested_attributes_for :contributors
accepts_nested_attributes_for :contributors_isbns
contributor.rb
attr_accessible :isbn_id
has_many :contributors_isbns
has_many :isbns, :through => :contributors_isbns
accepts_nested_attributes_for :isbns
contributors_isbn.rb
class ContributorsIsbn
attr_accessible :isbn_id, :contributor_id
belongs_to :isbn
belongs_to :contributor
accepts_nested_attributes_for :contributors
In the isbns controller:
def new
#isbn = Isbn.new
#title = "Create new ISBN"
1.times {#isbn.contributors.build}
#isbn.contributors_isbns.build.build_contributor
end
(obviously I can't make my mind up on which build method to use.)
In the isbns new.html.erb view:
<%= nested_form_for #isbn, :validate => false do |f| %>
<h1>Create new ISBN</h1>
<%= render 'shared/error_messages', :object => f.object %>
<%= render 'fields', :f => f %>
<div class="actions">
<%= f.submit "Create" %>
</div>
<% end %>
In the _fields partial, a version with a very plain text_field:
<%= field_set_tag 'Contributor' do %>
<%= f.link_to_add "Add Additional Contributor", :contributors %>
<li>
<%= f.label 'Contributor Sequence Number' %>
<%= f.text_field :descriptivedetail_contributor_sequencenumber%>
</li>
<%= f.fields_for :contributors_isbns, :validate => false do |contrib| %>
<li>
<%= contrib.label :id, 'contributors_isbns id' %>
<%= contrib.text_field :id %>
</li>
<% end %>
<li>
<%= f.label 'Contributor Role' %>
<%= f.text_field :descriptivedetail_contributor_contributorrole %>
</li>
<% end %>
And here, a fancier version which doesn't work either:
<%= f.fields_for :contributors_isbns, :validate => false do |contributors| %>
<li>
<%= f.label :personnameinverted, 'Contributor Name' %>
<%= f.collection_select(:contributor_id, Contributor.all, :id, :personnameinverted ) %>
</li>
<% end %>
This code uses the answer from here. Both result in a 'Missing block" error on the nested_form_for #isbn line.
Thanks so much again in advance.
Update: here is some info about the nested_form gem which might come in handy for looking at this sort of problem. And here's a [2009 but still relevant post][4] on accepts_nested_attributes_for.
Update 2: well, here's a thing. I've been poking around on a cut-down version of this in two different models, not using collection_select or has_many through, but just on a simple belongs_to / has_many association. The parent model is Contract and the child model is Istc. I couldn't even create a record through the nested form. However, after looking in the stack and googling the error message "Warning. Can't mass-assign protected attributes" I've just added :istcs_attributes to my :attr_accessible line and now I can add records. A rather crucial bit missing, and a case of RTFM, as it's right there in the gem readme. I'll update later to see if this works on the more complicated has_many through association.
Update 4: [Here][5] is another useful post about how to deal with a nil record error message.
Update 5: Slight detour - When I dynamically added a new set of fields to the form, one one of the child records was being created. Duh - I had the "Add" link inside the child forms area. Here's the before:
<%= f.fields_for :istcs do |istc_form| %>
<h4> Istc</h4>
<%= istc_form.label "istc name" %>
<%= istc_form.text_field :title_title_text %>
<%= istc_form.link_to_remove "[-] Remove this istc"%>
<%= f.link_to_add "[+] Add an istc", :istcs %>
<% end %>
and here's the after:
<%= f.fields_for :istcs do |istc_form| %>
<h4> Istc</h4>
<%= istc_form.label "istc name" %>
<%= istc_form.text_field :title_title_text %>
<%= istc_form.link_to_remove "[-] Remove this istc"%>
<% end %>
<%= f.link_to_add "[+] Add an istc", :istcs %>
Update 6, post-answer:
Oh noes. The collection_select isn't working. It's adding new contributor records, not using an existing one from the contributor model. Someone else had this problem too. Any ideas?
Huzzah! Here's the code which makes all this work. Bit verbose but didn't want to leave anything out. My main learnings:
you need to make the child attributes attr_accessible in the parent model
you need to make the parent and child ids attr_accessible in the join table model
it makes life easier if you build at least one child instance in the parent controller.
contributor.rb model
class Contributor < ActiveRecord::Base
attr_accessible #nothing relevant
has_many :contributors_isbns
has_many :isbns, :through => :contributors_isbns
isbn.rb model
class Isbn < ActiveRecord::Base
attr_accessible :contributors_attributes, :contributor_id, :istc_id #etc
belongs_to :istc
has_many :contributors, :through => :contributors_isbns
has_many :contributors_isbns
accepts_nested_attributes_for :contributors #if you omit this you get a missing block error
contributors_isbn model
class ContributorsIsbn < ActiveRecord::Base
belongs_to :isbn
belongs_to :contributor
attr_accessible :isbn_id, :contributor_id
isbn controller
def new
#isbn = Isbn.new
#title = "Create new ISBN"
1.times {#isbn.contributors.build}
end
new.html.erb
<td class="main">
<%= nested_form_for #isbn, :validate => false do |f| %>
<h1>Create new ISBN</h1>
<%= render 'shared/error_messages', :object => f.object %>
<%= render 'fields', :f => f %>
<div class="actions">
<%= f.submit "Create" %>
</div>
<% end %>
_fields.html.erb
<%= field_set_tag 'Identifier Details' do %>
<li>
<%= f.label 'Title prefix' %>
<%= f.text_field :descriptivedetail_titledetail_titleelement_titleprefix %>
</li>
<li>
<%= f.label 'Title without prefix' %>
<%= f.text_field :descriptivedetail_titledetail_titleelement_titlewithoutprefix %>
</li>
<li>
<%= f.label 'ISTC' %>
<%= f.collection_select(:istc_id, Istc.all, :id, :title_title_text, :prompt => true) %>
</li>
<% end %>
<%= field_set_tag 'Contributor' do %>
<li>
<%= f.label 'Contributor Sequence Number' %>
<%= f.text_field :descriptivedetail_contributor_sequencenumber%>
</li>
<%= f.fields_for :contributors, :validate => false do |contributor_form| %>
<li>
<%= contributor_form.label :personnameinverted, 'Contributor Name' %>
<%= contributor_form.collection_select(:isbn_id, Contributor.all, :id, :personnameinverted ) %>
</li>
<%= contributor_form.link_to_remove "[-] Remove this contributor"%>
<% end %>
<%= f.link_to_add "[+] Add a contributor", :contributors %>
<li>
<%= f.label 'Contributor Role' %>
<%= f.text_field :descriptivedetail_contributor_contributorrole %>
</li>
<% end %>

Resources