Building survey from railscast in rails 5 - ruby-on-rails

I have been following both the old and revised railscasts & this for something that i have to make along the same lines
I followed it upto some point but neither are the questions being displayed on the form nor are the answers getting added . Following is my model code
answers.rb
class Answer < ActiveRecord::Base
attr_accessor :content, :question_id
belongs_to :question
end
surveys.rb
class Survey < ApplicationRecord
attr_accessor :name, :questions_attributes
has_many :questions
accepts_nested_attributes_for :questions, allow_destroy: true
end
questions.rb
class Question < ApplicationRecord
attr_accessor :content, :survey_id, :answers_attributes
belongs_to :survey
has_many :answers
accepts_nested_attributes_for :answers, allow_destroy: true
end
Surveys Controller
class SurveysController < ApplicationController
before_action :set_survey, only: [:show, :edit, :update, :destroy]
# GET /surveys
# GET /surveys.json
def index
#surveys = Survey.all
end
# GET /surveys/1
# GET /surveys/1.json
def show
end
# GET /surveys/new
def new
#survey = Survey.new
3.times do
question = #survey.questions.build
4.times { question.answers.build }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_survey
#survey = Survey.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def survey_params
params.require(:survey).permit(:name, :question_id)
end
end
Views
_form.html.erb
<%= f.fields_for :questions do |builder| %>
<%= render 'question_fields', f: builder %>
<% end %>
<%= link_to_add_fields "Add Question", f, :questions %>
_question_fields.html.erb
<fieldset>
<%= f.label :content, "Question" %><br />
<%= f.text_area :content %><br />
<%= f.check_box :_destroy %>
<%= f.label :_destroy, "Remove Question" %>
<%= f.fields_for :answers do |builder| %>
<%= render 'answer_fields', f: builder %>
<% end %>
<%= link_to_add_fields "Add Answer", f, :answers %>
</fieldset>
_answers_fields.html.erb
<p>
<%= f.label :content, "Answer" %>
<%= f.text_field :content %>
<%= f.check_box :_destroy %>
<%= f.label :_destroy, "Remove" %>
</p>
show.html.erb
<p id="notice"><%= notice %></p>
<p>
<strong>Name:</strong>
<%= #survey.name %>
</p>
<ol>
<% for question in #survey.questions %>
<li><%= h question.content %></li>
<% end %>
</ol>
<p>
<%= link_to "Edit", edit_survey_path(#survey) %> |
<%= link_to "Destroy", #survey, :confirm => 'Are you sure?', :method => :delete %> |
<%= link_to "View All", surveys_path %>
</p>
Migrations
class CreateSurveys < ActiveRecord::Migration[5.0]
def change
create_table :surveys do |t|
t.string :name
t.timestamps
end
end
end
class CreateQuestions < ActiveRecord::Migration[5.0]
def change
create_table :questions do |t|
t.string :survey_id
t.string :integer
t.text :content
t.timestamps
end
end
end
Is there anything else i am missing out that needs to be done in rails 5 , i have been hours at this and it still confuses me why does it show me this error - Table 'app.answers' doesn't exist when i am calling answers from nested form . Any help in this regard would be very much appreciated .

The main issue here is it looks like you forgot an 'answer' migration to create the tables, create that and run it and should fix things up.
Additionally, those attr_accessor calls are going to be messing things up. They were required in older versions of Rails, but aren't anymore and now just serve throw things off. Example
With attr_accessor code
post = Post.new(title: "Something")
#=> #<Post id: nil, title: nil, created_at: nil, updated_at: nil>
post.title = "Something"
#=> "Something"
puts post
#=> #<Post id: nil, title: nil, created_at: nil, updated_at: nil>
Without
post = Post.new(title: "Something")
#=> #<Post id: nil, title: "Something", created_at: nil, updated_at: nil>
post.title = "Something Else"
#=> "Something Else"
puts post
#=> #<Post id: nil, title: "Something Else", created_at: nil, updated_at: nil>
As you can see, the first block, where my Post model had the attr_accessor for the title attribute, nothing was working as expected; I couldn't update the title. Once I removed it, things started to work as they should.
Based on the chat discussion, your _form.html.erb is missing form_for tag, and should looks something like
<%= form_for #survey do |f| %>
<%= f.label :name %><br />
<%= f.text_field :name %>
<!-- your current code here -->
<% end %>
you've got _answers_field.html.erb and in _question_fields.html.erb are calling
<%= render 'answer_fields', f: builder %>
Notice, the plural/singular mismatch.
and lastly, in your controller, you aren't permitting the nested attribute params which should end up looking like (unless I'm mistaken)
def survey_params
params.require(:survey).permit(:name, :question_attributes: [:id, :content, :_destroy, answer_attributes: [:id, :content, :_destroy])
end
Last couple of issues from chat were that the associations needed inverse_of because belongs_to is required by default in rails 5. And the last minor thing is that Answer is currently inheriting ActiveRecord::Base and the other models ApplicationRecord

Related

Unpermitted parameter: ROR

I am new in ror and when I submit my form:
<%= form_for :project, url: projects_path, html: {id:'form'} do |f| %>
<%= f.text_field :text, placeholder: 'Новая задача' %>
<%= link_to 'Отмена', '', id:'cancel_link' %>
<%= link_to 'Отправить', projects_path, id:'submit_link' %>
<% end %>
Have error:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"OR2HWCi3zVz9gB5VAmnzbEuzIwFGE58JlLrWQdNcws6FVTzqh5Cu0zvUJTUEv2O/sCvU9HuadJYr3mfA40ehGA==", "project"=>{"text"=>"NEW ITEM"}} Unpermitted parameter: :text
Have two models:
class Project < ApplicationRecord
has_many :todos
validates :title, presence: true
accepts_nested_attributes_for :todos
end
class Todo < ApplicationRecord
belongs_to :project, required: false
end
The Todo model has a text attribute in which our todo should be located
Controller
class ProjectsController < ApplicationController
def index
#projects = Project.all
end
def create
#project = Project.new(project_params)
if #project.save
redirect_to root_path
end
end
def update
end
private
def project_params
params.require(:project).permit(:title, todos_attributes: [:id, :text])
end
end
Project db
class CreateProjects < ActiveRecord::Migration[5.2]
def change
create_table :projects do |t|
t.string :title
t.string :todos
t.timestamps
end
Todo db
class CreateTodos < ActiveRecord::Migration[5.2]
def change
create_table :todos do |t|
t.text :text
t.boolean :isCompleted
t.integer :project_id
t.timestamps
end
I'm requesting the todo attributes using accepts_nested_attributes_for: todos, the controller is also registered on the guides, in project_params I request todos_attributes. But when sending a form to the database, the value is text. He does not save in db. Can u help please
In order to save text field in Todo model, you have to create nested form. Use nested_form gem for this purpose.
A vague example to show how it works:
<%= nested_form_for :project, url: projects_path, html: { id: 'form' } do |f| %>
<%= f.text_field :title, placeholder: 'Новая задача' %>
<%= f.fields_for :todos do |todo_form| %>
<%= todo_form.text_field :text %>
<%= todo_form.link_to_remove "Remove this todo" %>
<% end %>
<p><%= f.link_to_add "Add a todo", :todos %></p>
<%= link_to 'Отмена', '', id:'cancel_link' %>
<%= link_to 'Отправить', projects_path, id:'submit_link' %>
<% end %>
In controller, to have the functionality of removing a todo in case of editing a project:
def project_params
params.require(:project).permit(:title, todos_attributes: [:id, :text, _destroy])
end
In the migration CreateProjects < ActiveRecord::Migration[5.2], I do not think that you require todos as a string.
The form which you created is wrong, you need to create a nestead_form
It is giving you and Unpermitted parameter error because the text is not a field of project model you can check this on your migration file. You need to change it to title because the title is the field of project model.
And for to create a nested form you need to do some changes in your form
<%= form_for :project, url: projects_path, html: {id:'form'} do |f| %>
<%= f.text_field :title, placeholder: 'Новая задача' %>
<%= f.fields_for :todos do |todo| %>
<%= f.text_field :text %>
<% end %>
<%= link_to 'Отмена', '', id:'cancel_link' %>
<%= link_to 'Отправить', projects_path, id:'submit_link' %>
<% end %>

Rails 4 trouble with accepts_nested_attributes_for

I am new to rails and I'm trying the accepts_nested_attributes_for function. I am creating an inventory system and the accepts_nested_attributes_for feature is being used to attach multiple order details to an order. An order must also be associated with store location.
The problem I'm having is the order is being created but no data is being passed to the order details table.
My views are below:
Orders View
<h1>Place An Order</h1>
<%= form_for ([#location, #order]) do |f| %>
<p>
<%= f.label :customer_id %><br />
<%= f.text_field :customer_id %>
</p>
<p>
<h3>Items</h3>
<%= f.fields_for :order_details do |builder| %>
<%= render 'order_detail_fields', :f => builder %>
<% end %>
</p>
<p><%= link_to_add_fields "Add Item", f, :order_details %></p>
<p>
<%= f.submit %>
</p>
<% end %>
Order_details_fields Partial
<p class="fields">
<%= f.label :item_id %><br />
<%= f.text_field :item_id %></br>
<%= f.label :quantity %></br>
<%= f.text_field :quantity %></br>
<%= f.label :cost %></br>
<%= f.text_field :cost %></br>
<%= f.label :discount %><br />
<%= f.text_field :discount %><br />
<%= f.hidden_field :_destroy %>
<%= link_to_function "remove", "remove_fields(this)" %>
</p>
Orders Controller
class OrdersController < ApplicationController
def index
#orders = Order.all
end
def show
#order = Order.find(params[:id])
end
def new
#order = Order.new
#location = Location.find(params[:location_id])
end
def create
#location = Location.find(params[:location_id])
#order = #location.orders.create(order_params)
##order = #order.order_details.create
if #order.save
redirect_to #order
else
render :action => 'new'
end
end
private
def order_params
params.require(:order).permit(:customer_id, order_detials_attributes: [:id, :item_id, :quantity, :cost, :discount])
end
end
Orders Model
class Order < ActiveRecord::Base
belongs_to :location
has_many :order_details, :dependent => :destroy
accepts_nested_attributes_for :order_details, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
end
Order Details Model
class OrderDetail < ActiveRecord::Base
belongs_to :order
end
Routes
resources :locations do
resources :orders
end
resources :orders do
resources :order_details
end
Any help with this would be greatly appreciated
Build
Looks like everything is right to me - the only problem being the issue #Pavan outlined, which is that when you use accepts_nested_attributes_for, you have to build the associative object, so it can be used in the form:
#app/controllers/orders_controller.rb
Class OrdersController < ApplicationController
def new
#location = Location.find parmas[:id]
#order = Order.find params[:id]
#order.order_details.build
end
end
Although this looks like the only issue you have, there may be other problems (validation on the OrderDetail model as an example (which you don't have)
The only issue with what both I and Pavan have recommended is if you don't build your associative data, the fields_for don't show on the form. If your fields are showing, it may be a different issue, which will be highlighted in the params hash

accepts_nested_attributes_for with hidden_field

Been searching stackoverflow for an answer to this one all day. I have a form to create a new topic. The first post should also be created with the topic. All is well except user_id is not being saved to the post.
Post Model
class Post < ActiveRecord::Base
belongs_to :topic
belongs_to :user
end
Topic Model
class Topic < ActiveRecord::Base
belongs_to :forum
belongs_to :user
has_many :posts
accepts_nested_attributes_for :posts
end
Post Controller
class PostsController < ApplicationController
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
if #post.save
redirect_to topic_path(#post.topic_id)
else
render 'new'
end
end
private
def post_params
params.require(:post).permit(:content, :topic_id, :topic_name, :user_id)
end
end
Topic Controller
class TopicsController < ApplicationController
def new
#topic = Topic.new
#topic.posts.build
end
def create
#topic = Topic.new(topic_params)
if #topic.save
redirect_to #topic
else
render 'new'
end
end
private
def topic_params
params.require(:topic).permit(
:topic_name,
:forum_id,
:user_id,
posts_attributes: [:id, :content, :topic_id, :topic_name, :user_id ] )
end
end
new/topic View
<%= form_for(#topic) do |f| %>
<%= f.hidden_field :forum_id, :value => params[:forum_id] %>
<%= f.hidden_field :user_id, :value => current_user.id %>
<%= f.label :topic_name %>
<%= f.text_field :topic_name %>
<%= f.fields_for :posts do |p| %>
<%= p.label :content %>
<%= p.text_area :content %>
<% end %>
<%= f.submit "Post Topic", class: "btn btn-large btn-success" %>
<% end %>
I am not entirely sure why the user_id is not being passed to the post. Hopefully someone smarter than me can help me learn what to do :)
UPDATE
I changed the strong params in my topics controller to this.
def topic_params
params.require(:topic).permit(
:topic_name,
:forum_id,
posts_attributes: [:content, :topic_id, :id, '_destroy' ] ).merge(:user_id => current_user.id, posts_attributes: [:user_id => current_user.id])
end
Now the user_id is working but none of the posts_attributes like :content are being saved. I'm having a lot of fun with this one..
Notice the form attributes that being generated in the browser, all the nested attributes for post have a prefix like topic[post_attributes], try change the form to:
<%= form_for(#topic) do |f| %>
<%= f.hidden_field :forum_id, :value => params[:forum_id] %>
<%= f.label :topic_name %>
<%= f.text_field :topic_name %>
<%= f.fields_for :posts do |p| %>
<%= p.hidden_field :user_id, :value => current_user.id %>
<%= p.label :content %>
<%= p.text_area :content %>
<% end %>
<%= f.submit "Post Topic", class: "btn btn-large btn-success" %>
<% end %>
Short answer, user_id is not in the posts_attributes since the only attributes there is content, which means that allowing other attributes like topic_id and topic_name is useless.
Now that we cleared that, you SHOULD NOT use a form input for the value of the creator of any model, because it's easy for anyone to tamper with the form and set the value to anything else, like other user's id. Alternatively, you should set the user_id value in the controller, in your case, the TopicsController. Here is the code:
def create
_params = topic_params.deep_merge(user: current_user, posts_attributes: {user: current_user})
#topic = Topic.new(_params)
if #topic.save
redirect_to #topic
else
render 'new'
end
end
and remove the user_id hidden field from the form.
UPDATE: Your last code update contains an error; it should be .merge(:user_id => current_user.id, posts_attributes: {:user_id => current_user.id}). You used a square brackets around :user_id => current_user.id instead of curly ones.

Set the IDs of two parent objects in a nested form / displayed twice

I have 5 Models,
Users, Jobs, Applications, Questions, and Answers
Jobs has many questions
Jobs and Users are associated through Applications
Both Questions and Applications has_many answers.
I'm trying to make an Application create action which will --
Associate a User to a particular job
Allow the user to answer the questions that the particular job has
Right now, I'm getting it to work, but it's displaying the Question and Answer Twice.
I.e The View comes out as -->
This is question one
Text field for question one
This is question two
Text field for question two
This is question one
Text field for question one
This is question two
Text field for question two
This is what my Application#New view looks like -->
<% provide(:title, " Apply to this job") %>
<%= form_for [#job, #application] do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<% #job.questions.each do |question| %>
<%= f.fields_for :answers do |question_field| %>
<%= question_field.label :content, question.content %>
<%= question_field.text_area :content %>
<%= question_field.hidden_field :question_id, :value => question.id %>
<% end %>
<% end %>
<%= f.submit "Submit the application", class: "button" %>
<% end %>
This is my Application Controller ->
class ApplicationsController < ApplicationController
before_filter :set_user_and_job
def new
job = params[:job_id]
#application = Application.build(job)
end
def create
#application = Application.new(application_params)
#application.save
redirect_to root_url, :notice => "You have now applied!"
end
def edit
#application = Application.find(params[:id])
#answers = []
#job.questions.each do |question|
#application.answers.each do |answer|
#answers << answer if answer.question_id == question.id
end
end
end
def update
#application = Application.find(params[:id])
#application.update_attributes(application_params)
redirect_to root_url, :notice => "You have updated your application!"
end
def destroy
Application.find(params[:id]).destroy
flash[:success] = "Application Deleted."
redirect_to root_url
end
def show
#application = Application.find(params[:id])
#answers = []
#job.questions.each do |question|
#application.answers.each do |answer|
#answers << answer if answer.question_id == question.id
end
end
end
private
def set_user_and_job
#user = current_user
#job = Job.find(params[:job_id])
end
def application_params
params.require(:application).permit(:job_id, :user_id,
answers_attributes:[:id, :question_id, :content]).merge(user_id: current_user.id,
job_id: params[:job_id])
end
end
This is my Application Model
# == Schema Information
#
# Table name: applications
#
# id :integer not null, primary key
# user_id :integer
# job_id :integer
# created_at :datetime
# updated_at :datetime
#
class Application < ActiveRecord::Base
belongs_to :job
belongs_to :user
validates :job_id, presence: true
validates :user_id, presence: true
has_many :answers
accepts_nested_attributes_for :answers, :allow_destroy => true
def self.build(job_id)
application = self.new
job = Job.find(job_id)
job.questions.count.times do
application.answers.build
end
application
end
end
This is my edit view(fully functioning) ->
<% provide(:title, " Edit this application") %>
<%= form_for [#job, #application] do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.fields_for :answers do |question_field| %>
<%= question_field.label :content, question_field.object.question.content %>
<%= question_field.text_area :content %>
<% end %>
<%= f.submit "Submit the application", class: "button" %>
<% end %>
I think the reason this is happening is that I'm running a double loop, but I'm not sure how else to also get the question id and question content for each answer.
What do you think?
--
Here's how the parameters look, when I run this form ->
<%= form_for [#job, #application] do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<% #job.questions.each do |question| %>
<%= f.fields_for :answers, question do |question_field| %>
<%= question_field.label :content, question.content %>
<%= question_field.text_area :content %>
<%= question_field.hidden_field :question_id, :value => question.id %>
<% end %>
<% end %>
<%= f.submit "Submit the application", class: "button" %>
<% end %>
Started POST "/jobs/3/applications" for 127.0.0.1 at 2013-12-30 14:26:35 +0400
Processing by ApplicationsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"+gbcaJJjQZ2GfkWiKcOmSf58hf/GEnWonmGrVe1p3ZI=", "application"=>{"question"=>{"content"=>"Sample answer 2", "question_id"=>"6"}}, "commit"=>"Submit the application", "job_id"=>"3"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."remember_token" = '69f212609955f368c0f17873b5dce9f506bd3eb7' LIMIT 1
Job Load (0.1ms) SELECT "jobs".* FROM "jobs" WHERE "jobs"."id" = ? LIMIT 1 [["id", "3"]]
Unpermitted parameters: question
(0.1ms) begin transaction
Try this:
<% #job.questions.each do |question| %>
<%= f.fields_for :answers, question do |question_field| %>
<%= question_field.label :content, question.content %>
<%= question_field.text_area :content %>
<%= question_field.hidden_field :question_id, :value => question.id %>
<% end %>
<% end %>
This should split up the f.fields_for call into different instances of the object
I think you're basically cycling through the questions, which is then showing all the fields, whereas if you make it work for a single instance, it will just show the answer for that question
Maybe Try has_many :through
Maybe we need to implement has_many :through on the answers fields, so that we can create an answer for each question, like this (sorry if I got some associations incorrect):
#app/models/question.rb
Class Question < ActiveRecord::Base
belongs_to :job
has_one :answer
accepts_nested_attributes_for :answer
end
#app/models/answer.rb
Class Answer < ActiveRecord::Base
belongs_to :question
belongs_to :user
end
#app/models/application.rb
Class Application < ActiveRecord::Base
belongs_to :user
belongs_to :job
has_many :questions, through: job #-> maybe
has_many :answers, through: :questions #-> maybe
accepts_nested_attributes_for :questions
def self.build(job_id)
application = self.new
job = Job.find(job_id)
job.questions.count.times do
application.questions.build.build_answer
end
application
end
end
#app/models/job.rb
Class Job < ActiveRecord::Base
has_many :questions
has_many :applications
has_many :answers, through: :applications
has_many :users, through: :applications
end
This will give you this view:
<%= form_for #application do |f| %>
<%= f.fields_for :questions do |q| %>
<%= q.label :content %>
<%= q.fields_for :answer do |a| %>
<%= a.text_area :content %>
<% end %>
<% end %>
<% end %>
You'd have to change your controller to handle the new associations like this:
#app/controllers/applications_controller.rb
def new
job = params[:job_id]
#application = Application.build(job)
end
private
def application_params
params.require(:application).permit(:job_id, :user_id,
questions_attributes: [answer_attributes:[:content]]).merge(user_id: current_user.id,
job_id: params[:job_id])
end

Form Helper Undefined Method

When I try to access the view, an error is returned:
undefined method 'title' for #Task id: nil, created_at: nil, updated_at: nil
tasks_controller.rb (Controller)
class TasksController < ApplicationController
def new
#task = Task.new
end
def create
#task = Task.new(params[:task])
if #task.save
redirect_to new_task_path
end
end
end
/tasks/new.html.erb (View)
<%= form_for :task, url: tasks_path do |f| %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :details %><br>
<%= f.text_area :details %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
task.rb (Model)
class Task < ActiveRecord::Base
belongs_to :user
attr_accessible :title, :details, :user_id, :volunteers
end
What should I do?
You have not defined fields in your database, see:
#Task id: nil, created_at: nil, updated_at: nil
There is no title nor details there, do this:
rails g migration add_title_and_details_to_tasks title details
Check that your migration file is correctly creating these 2 fields.
Then run rake db:migrate. Next time remember to generate your resource with these fields:
rails g scaffold Task title details
This way, when you migrate your fields will be there.
It looks like you have pending migration(s) (do you have title in your schema.rb).
other note: build your form for #task

Resources