I am trying to run through a simple nested_attributes exercise, where a question has many answers (how like life is that?). I can't get the answers field to show up in either the 'new' or the 'edit' actions. As far as I can tell, I'm following the specs precisely. Clearly, this is not the case. Any pointers appreciated, thanks.
Models
# question.rb
has_many :answers
accepts_nested_attributes_for :answers
# answer.rb
belongs_to :question
View
# _form.html.erb
<%= form_for(#question) do |f| %>
...
<div class="field">
<%= f.label :content, 'Question' %><br>
<%= f.text_field :content %>
</div>
<div class="field">
<% f.fields_for :answers do |ff| %>
<%= ff.label :content, 'Answer' %><br>
<%= ff.text_field :content %>
<% end %>
</div>
...
<% end %>
Controller
# questions_controller.rb
def new
#question = Question.new
#question.answers.build
end
def edit
end
def create
#question = Question.new(question_params)
respond_to do |format|
if #question.save
format.html { redirect_to #question, notice: 'Question was successfully created.' }
format.json { render :show, status: :created, location: #question }
else
format.html { render :new }
format.json { render json: #question.errors, status: :unprocessable_entity }
end
end
end
private
def question_params
params.require(:question).permit(:content, answer_attributes: [:id, :content, :question_id] )
end
Use <%=, not <%, otherwise the resulting output from the field_for Form Builder won't be output to the form.
<%= f.fields_for :answers do |ff| %>
Now I just need to figure out why the nested attribute (the answer)
isn't saving to the database.
Extending #Dylan Markow's answer.
Since it is has_many answers,it should be answers_attributes instead of answer_attributes in your question_params method.
def question_params
params.require(:question).permit(:content, answers_attributes: [:id, :content, :question_id] )
end
Related
I am building simple ROR app which has survey question and answers. Survey is generated using scaffolding method while question and answer are model only.
Survey.rb
class Survey < ApplicationRecord
has_many :questions
accepts_nested_attributes_for :questions, allow_destroy: true
validates :name, presence: true
end
Question.rb
class Question < ApplicationRecord
belongs_to :survey
has_many :answers
accepts_nested_attributes_for :answers
validates :question_content, presence: true
end
Answer.rb
class Answer < ApplicationRecord
belongs_to :question
end
survey_controller.rb
class SurveysController < ApplicationController
before_action :set_survey, only: [:show, :edit, :update, :destroy]
def index
#surveys = Survey.all
end
def show
#survey= Survey.find(params[:id])
end
def new
#survey = Survey.new
#questions = #survey.questions.new
#answers = #questions.answers.new
end
def edit
end
def create
#survey = Survey.new(survey_params)
Survey.create(survey_params)
respond_to do |format|
if #survey.save
format.html { redirect_to #survey, notice: 'Survey was successfully created.' }
format.json { render :show, status: :created, location: #survey }
else
format.html { render :new }
format.json { render json: #survey.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #survey.update(survey_params)
format.html { redirect_to #survey, notice: 'Survey was successfully updated.' }
format.json { render :show, status: :ok, location: #survey }
else
format.html { render :edit }
format.json { render json: #survey.errors, status: :unprocessable_entity }
end
end
end
def destroy
#survey.destroy
respond_to do |format|
format.html { redirect_to surveys_url, notice: 'Survey was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_survey
#survey = Survey.find(params[:id])
end
def survey_params
params.require(:survey).permit(:name, questions_attributes: [:id, :question_content], answers_attributes: [:id, :answer_content, :answer_type])
end
end
Survey form partial
<%= form_for(#survey) do |form| %>
<% if survey.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(survey.errors.count, "error") %> prohibited this survey from being saved:</h2>
<ul>
<% survey.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= form.label :name %>
<%= form.text_field :name, id: :survey_name %>
</div>
<%= form.fields_for :questions do |builder| %>
<fieldset>
<%= builder.label :question_content, "Question" %><br/>
<%= builder.text_area :question_content %><br/>
<%= form.fields_for :answers do |f| %>
<fieldset>
<%= f.text_area :answer_type %>
<%= f.text_area :answer_content %><br/>
</fieldset>
</fieldset>
<% end %>
<%end %>
<div class="actions">
<%= form.submit %>
</div>
I want to implement question and answer in single page i.e survey. Any help will appreciated.
Problem : Unable to save answers_attributes to database but Question attributes work perfectly fine. I am probably making mistake somewhere in controller not sure.
You need to change the way you're building the answer fields, so that they're referenced as fields_for questions, not surveys. Try changing this piece of code:
<%= form.fields_for :questions do |builder| %>
<fieldset>
<%= builder.label :question_content, "Question" %><br/>
<%= builder.text_area :question_content %><br/>
<%= form.fields_for :answers do |f| %>
<fieldset>
<%= f.text_area :answer_type %>
<%= f.text_area :answer_content %><br/>
</fieldset>
</fieldset>
<% end %>
<%end %>
to this:
<%= form.fields_for :questions do |builder| %>
<fieldset>
<%= builder.label :question_content, "Question" %><br/>
<%= builder.text_area :question_content %><br/>
<%= builder.fields_for :answers do |f| %>
<fieldset>
<%= f.text_area :answer_type %>
<%= f.text_area :answer_content %><br/>
</fieldset>
<% end %>
</fieldset>
<% end %>
(Note: the meaningful change is changing form.fields_for to builder.fields_for)
I'm trying to create a nested form in Ruby on Rails. The form appears as expected. But when it is saved the nested attribute, Booking, is not saved.
cleaner.rb
class Cleaner < ActiveRecord::Base
validates_presence_of :first_name
validates_presence_of :last_name
validates_presence_of :quality_score
validates :quality_score, inclusion: 0.0...5.0
has_many :assignments
has_many :bookings
has_many :cities, through: :assignments
has_many :customers, through: :bookings
end
customer.rb
class Customer < ActiveRecord::Base
validates_presence_of :first_name
validates_presence_of :last_name
validates_uniqueness_of :phone_number
belongs_to :city
has_many :bookings
has_many :cleaners, through: :bookings
accepts_nested_attributes_for :bookings
end
booking.rb
class Booking < ActiveRecord::Base
belongs_to :customer
belongs_to :cleaner
validates_presence_of :customer
validates_presence_of :cleaner
validates_presence_of :date
end
customers_controller.rb
class CustomersController < ApplicationController
before_action :set_customer, only: %i[show edit update destroy]
def index
#customers = Customer.all
end
def show; end
def new
#customer = Customer.new
end
def edit; end
def create
#customer = Customer.find_or_initialize_by(phone_number: params[:phone_number])
#customer.assign_attributes(customer_params)
respond_to do |format|
if #customer.save
format.html { redirect_to #customer, notice: 'Customer was successfully created.' }
format.json { render :show, status: :created, location: #customer }
else
format.html { render :new }
format.json { render json: #customer.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #customer.update(customer_params)
format.html { redirect_to #customer, notice: 'Customer was successfully updated.' }
format.json { render :show, status: :ok, location: #customer }
else
format.html { render :edit }
format.json { render json: #customer.errors, status: :unprocessable_entity }
end
end
end
def destroy
#customer.destroy
respond_to do |format|
format.html { redirect_to customers_url, notice: 'Customer was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_customer
#customer = Customer.find(params[:id])
end
def customer_params
params.require(:customer).permit(:first_name, :last_name, :phone_number, :city, :city_id, bookings_attributes: %i[cleaner_id date])
end
end
HomeController.rb
class HomeController < ApplicationController
def index
#customer = Customer.new
#customer.bookings.build
end
end
Parameters
{"utf8"=>"✓", "authenticity_token"=>"c4xo2M4r57+/xBsmcc+7yajpQU13u1kiwmOthx/nP7HiJXJIfS9/OqC0MrWCcaDrSW/xN8UGk2+LVfnUnbTb3A==", "customer"=>{"first_name"=>"adfad", "last_name"=>"fad", "phone_number"=>"9392323", "city_id"=>"1", "bookings_attributes"=>{"0"=>{"cleaner_id"=>"1"}}}, "#<ActionView::Helpers::FormBuilder:0x007f86bec96480>"=>{"date(1i)"=>"2017", "date(2i)"=>"8", "date(3i)"=>"2"}, "commit"=>"Create Customer"}
updated form
<h1>Sign Up Now</h1>
<%= form_for #customer do |f| %>
<% if #customer.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#customer.errors.count, "error") %> prohibited this customer from being saved:</h2>
<ul>
<% #customer.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :first_name %><br>
<%= f.text_field :first_name %>
</div>
<div class="field">
<%= f.label :last_name %><br>
<%= f.text_field :last_name %>
</div>
<div class="field">
<%= f.label :phone_number %><br>
<%= f.text_field :phone_number %>
</div>
<div class="field">
<%= f.label :city %><br>
<%= f.select :city_id, options_for_select(City.pluck(:name, :id)) %>
</div>
<%= f.fields_for :bookings do |b| %>
<%= b.date_select :date %>
<br>
<%= b.select :cleaner_id, Cleaner.all.pluck(:first_name, :id) %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
It's now failing with 2 errors
Bookings customer can't be blank
Bookings date can't be blank
use the iteration of the first #customer so rails can read properly
i saw this example https://www.sitepoint.com/complex-rails-forms-with-nested-attributes/
<%= f.fields_for :booking do |ff| %>
<%= ff.select :city_id,options_for_select(City.pluck(:name, :id)) %>
i hope it works cross finger :)
I am trying to build a form for a has_many :through relationship. The Problem is, that I can access the attributes from the join table (:through) in the form, but not the other table.
My models look like this:
class Recipe < ActiveRecord::Base
has_many :quantities
has_many :ingredients, through: :quantities
accepts_nested_attributes_for :quantities
end
class Quantity < ActiveRecord::Base
belongs_to :recipe
belongs_to :ingredient
end
class Ingredient < ActiveRecord::Base
has_many :quantities
has_many :recipes, through: :quantities
end
My Form:
<%= form_for(#recipe) do |f| %>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>
<div class="quantities">
<%= f.fields_for :quantities do |builder| %>
<%= render 'quantity_fields', :f => builder%>
<% end %>
<%= link_to_add_association 'Add', f, :quantities %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
And the quantity_fields partial:
<p>
<%= f.label :value, "Value" %>
<%= f.text_field :value %>
<%= f.label :unit, "Unit" %>
<%= f.text_field :unit %>
<%= f.text_field :ingredient %>
<%= f.fields_for :ingredient do |builder|%>
<%= builder.label :name, "Ingredient"%>
<%= builder.text_field :name %>
<% end %>
</p>
The fields for the quantities attributes are correct, but the text field for the ingredient name (within the quantity_fields partial) stays empty.
On the Rails console on the other hand I can easily use the ingredient method on a quantity object and get a result.
I try to find a solution to this since a while now, and it becomes really frustrating to me. I am sure this is a standard task and I am just missing a small part. Can anyone help me?
Edit:
Also when I change my recipe form to directly render the ingredients, it doesn't work. The text fields remain empty.
<%= form_for(#recipe) do |f| %>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>
<div class="ingredients">
<%= f.fields_for :ingredients do |builder| %>
<p>
<%= builder.label :name, "Name" %>
<%= builder.text_field :name %>
<%= builder.label :description, "Description" %>
<%= builder.text_field :description %>
</p>
<% end %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Edit 2:
The recipes controller:
class RecipesController < ApplicationController
before_action :set_recipe, only: [:show, :edit, :update, :destroy]
# GET /recipes
# GET /recipes.json
def index
#recipes = Recipe.all
end
# GET /recipes/1
# GET /recipes/1.json
def show
end
# GET /recipes/new
def new
#recipe = Recipe.new
end
# GET /recipes/1/edit
def edit
end
# POST /recipes
# POST /recipes.json
def create
#recipe = Recipe.new(recipe_params)
respond_to do |format|
if #recipe.save
format.html { redirect_to #recipe, notice: 'Recipe was successfully created.' }
format.json { render :show, status: :created, location: #recipe }
else
format.html { render :new }
format.json { render json: #recipe.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /recipes/1
# PATCH/PUT /recipes/1.json
def update
respond_to do |format|
if #recipe.update(recipe_params)
format.html { redirect_to #recipe, notice: 'Recipe was successfully updated.' }
format.json { render :show, status: :ok, location: #recipe }
else
format.html { render :edit }
format.json { render json: #recipe.errors, status: :unprocessable_entity }
end
end
end
# DELETE /recipes/1
# DELETE /recipes/1.json
def destroy
#recipe.destroy
respond_to do |format|
format.html { redirect_to recipes_url, notice: 'Recipe was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_recipe
#recipe = Recipe.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def recipe_params
params.require(:recipe).permit(:title, :description, quantities_attributes: [:value, :unit, :recipe, :ingredient, :_destroy])
end
end
When utilizing a has_many :through association in that way, you can set up your models like this:
class Recipe < ActiveRecord::Base
has_many :quantities
has_many :ingredients, inverse_of: :recipes
accepts_nested_attributes_for :quantities
end
class Quantity < ActiveRecord::Base
belongs_to :recipe
belongs_to :ingredient
accepts_nested_attributes_for :ingredients
end
class Ingredient < ActiveRecord::Base
has_many :quantities
has_many :recipes, inverse_of: :ingredients
end
This article covers nested attributes with has_many :through associations in a similar context. Hope this helps!
I'm having problem with some attributes which are not a part of my model. I'm trying to design a simple payment page as you can see below, all the fiels are represented in my database, except card_number and card_verification for security reasons.
When I load the page it is throwing an error:
undefined method `card_number' for #<Order:0x00000004eddb00>
undefined method `card_verification' for #<Order:0x00000004eddb00>
What's wrong with my form?
Thanks.
The Form:
<%= simple_form_for(#order, html:{class: "well"}) do |f| %>
<%= f.input :first_name %>
<%= f.input :last_name %>
<%= f.input :card_type, collection: ["Visa", "MasterCard"] %>
<%= f.input :card_expires_on %>
<%= f.input :card_number %>
<%= f.input :card_verification %>
<%= f.button :submit %>
<% end %>
order.rb:
class Order < ActiveRecord::Base
belongs_to :user
belongs_to :participation
end
orders_controller.rb:
class OrdersController < ApplicationController
before_action :set_order, only: [:show, :edit, :update, :destroy]
def new
#order = Order.new
end
def create
#order = Order.new(order_params)
respond_to do |format|
if #order.save
format.html { redirect_to #order, notice: 'Order was successfully created.' }
format.json { render action: 'show', status: :created, location: #order }
else
format.html { render action: 'new' }
format.json { render json: #order.errors, status: :unprocessable_entity }
end
end
end
private
def set_order
#order = Order.find(params[:id])
end
def order_params
params.require(:order).permit(:ip_address, :first_name, :last_name, :card_type, :card_expires_on, :user_id, :participation_id)
end
end
I think the part that is throwing the error should be this in your form:
<%= f.input :card_number %>
<%= f.input :card_verification %>
You could try adding:
attr_accessor :card_number, :card_verification
to your model.
I understand some railscasts may be old, but revised shouldn't be. I am trying really hard to overcome nested form, but most answer i get is use a plugin. So i try to make the railcasts from scratch but an exception occurs. I am wondering what would be the Railcasts 196 Updated Version has today with proper code.
Here my code, maybe its a silly mistake from me.
Survey Model
class Survey < ActiveRecord::Base
has_many :questions, :dependent => :destroy
accepts_nested_attributes_for :questions , :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
attr_accessible :name, :questions_attributes
end
Question Model
class Question < ActiveRecord::Base
belongs_to :survey
attr_accessible :content, :question_id
accepts_nested_attributes_for :answers , :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
attr_accessible :name, :answers_attributes
end
Answer Model
class Answer < ActiveRecord::Base
belongs_to :questions
attr_accessible :content, :question_id
end
Survey Show Form
<p id="notice"><%= notice %></p>
<div>Name:</div>
<div><%=#survey.name%></div>
<% for question in #survey.questions %>
<div><%=h question.content%></div>
<% end %>
<%= link_to 'Edit', edit_survey_path(#survey) %> |
<%= link_to 'Back', surveys_path %>
Form.html.erb
<%= form_for(#survey) do |f| %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<%= f.fields_for :questions do |bf|%>
<% render 'question_fields, :f => bf %>
<% end %>
***<div class="actions">***
<%= f.submit %>
</div>
<% end %>
question_field Form
<%= f.label :content, "Question" %><br />
<%= f.text_area :content, :rows=> 3 %><br />
<%= f.check_box :_destroy %>
<%= f.label :_destroy, "Remove Question"%><br />
<%= f.fields_for :answer do |form| %>
<%= render 'answer_fields', :f => form %>
<% end %>
answer_field Form
<%= f.label :content, "Answer" %>
<%= f.text_field :content %>
<%= f.check_box :_destroy %>
<%= f.label :_destroy, "Remove" %>
Survey Controller
class SurveysController < ApplicationController
# GET /surveys
# GET /surveys.json
def index
#surveys = Survey.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #surveys }
end
end
# GET /surveys/1
# GET /surveys/1.json
def show
#survey = Survey.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #survey }
end
end
# GET /surveys/new
# GET /surveys/new.json
def new
#survey = Survey.new
3.times {#survey.questions.build }
respond_to do |format|
format.html # new.html.erb
format.json { render json: #survey }
end
end
# GET /surveys/1/edit
def edit
#survey = Survey.find(params[:id])
end
# POST /surveys
# POST /surveys.json
def create
#survey = Survey.new(params[:survey])
respond_to do |format|
if #survey.save
format.html { redirect_to #survey, notice: 'Survey was successfully created.' }
format.json { render json: #survey, status: :created, location: #survey }
else
format.html { render action: "new" }
format.json { render json: #survey.errors, status: :unprocessable_entity }
end
end
end
# PUT /surveys/1
# PUT /surveys/1.json
def update
#survey = Survey.find(params[:id])
respond_to do |format|
if #survey.update_attributes(params[:survey])
format.html { redirect_to #survey, notice: 'Survey was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #survey.errors, status: :unprocessable_entity }
end
end
end
# DELETE /surveys/1
# DELETE /surveys/1.json
def destroy
#survey = Survey.find(params[:id])
#survey.destroy
respond_to do |format|
format.html { redirect_to surveys_url }
format.json { head :no_content }
end
end
end
Here my error when creating a new survey
Not sure what else could be wrong
ArgumentError in SurveysController#create
No association found for name `answers'. Has it been defined yet?
Here the new error
SyntaxError in Surveys#new
Showing /home/jean/rail/surveysays/app/views/surveys/_form.html.erb where line #22 raised:
/home/jean/rail/surveysays/app/views/surveys/_form.html.erb:22: syntax error, unexpected keyword_class, expecting keyword_do or '{' or '('
#output_buffer.safe_concat(' <div class="actions">
^
/home/jean/rail/surveysays/app/views/surveys/_form.html.erb:24: unterminated regexp meets end of file
/home/jean/rail/surveysays/app/views/surveys/_form.html.erb:24: syntax error, unexpected $end, expecting keyword_end
Add has_many :answers to the Question model and change the Answer model to belongs_to :question.