I have 3 models Item which accepts nested attributes for questions and questions accept nested attributes for answers. I'm trying to create an item which has a question and an answer in the same form.
item.rb
class Item < ActiveRecord::Base
has_many :questions, dependent: :destroy
accepts_nested_attributes_for :questions
end
question.rb
class Question < ActiveRecord::Base
belongs_to :item
has_many :answers, dependent: :destroy
accepts_nested_attributes_for :answers
end
answer.rb
class Answer < ActiveRecord::Base
belongs_to :question
end
item_controller.rb
class ItemsController < ApplicationController
def new
#item = #repository.items.new
questions = #item.questions.build
answers = questions.answers.build
end
def create
#item = Item.new(item_params)
if #item.save
redirect_to #item, notice: '...'
else
render action: 'new'
end
end
private
def item_params
params.require(:item).permit(:id, :content, :kind, :questions_attributes => [:content, :helper_text, :kind], :answers_attributes => [:content, :correct])
end
end
_form.haml
= simple_form_for(#item) do |f|
= f.input :kind
= f.input :content
= f.simple_fields_for :questions do |q|
= q.input :content
= q.simple_fields_for :answers do |a|
= a.input :content
= f.submit
The form is being displayed correctly and it saves the question model correctly. I can't seem to save the answer though.
I've already looked at a lot of online help but none are covering it with Rails 4 strong params.
I think your problem is with your strong params:
def item_params
params.require(:item).permit(:id, :content, :kind, questions_attributes: [:content, :helper_text, :kind, answers_attributes: [:content, :correct]])
end
Basically, when you pass a deep nested form (where you have multiple dependent models), you'll have to pass the attributes as part of the other model's attributes. You had the params as separate
I run into a similar issue and, while Richard Peck answer helped me as well, there is one thing that it was missing for me.
If you are deep-nesting you need to specify the id of the parent of the nested item. In this case to create an answers you need to make questions id explicit with q.input :id, otherwise you will run into this error.
= simple_form_for(#item) do |f|
= ...
= f.simple_fields_for :questions do |q|
= ...
= q.input :id
= q.simple_fields_for :answers do |a|
= ...
= f.submit
Related
Now I'm making rails app for survey. I like to build answer fields for every questions automatically. But I can't make it.
My model
class Survey
has_many :answers
accepts_nested_attributes_for :answers, allow_destroy: true
end
class Question
hasmany :answers
end
class Answers
belongs_to :survey
belongs_to :question
end
My controller
class SurveyController
def new
#survey = Survey.new
question_length = Questions.length
question_length.times{#survey.answers.build}
end
end
My view
= simple_form_for(#survey) do |f|
= f.input :input_date, as: :date
= f.simple_fields_for :answers do |ff|
= ff.input :question_id, collection: questions.all.map{ |q| [q.title, q.id] }
= ff.input :answer_text
It's working now like this, and answer fields are built for every questions. But it makes user to select question. So I want question_id built automatically for ever questions.
I've tried this controller
def new
#survey = Survey.new
question_length = Questions.length
question_array = Questions.all.map{|q| q.id}
question_length.times{#survey.answers.build(question_id: question_array)}
end
But it doesn't work. If somebody knows how to make it, please advise me.
Regards
I got answer.
class SurveyController
def new
#survey = Survey.new
#survey.answers.build
end
end
- #questions.each do |questions|
= simple_form_for(#survey) do |f|
= f.input :input_date, as: :date
= f.simple_fields_for :answers do |ff|
%p= questions.title
= ff.hidden_field :question_id, value: question.id
= ff.input :answer_text
It's not perfect answer. But it's fine. Thanks watching my question.
I am using Rails 5.1 and im having some issues saving params on an n:n relationship.
I have three models:
class Course < ApplicationRecord
belongs_to :studio
has_many :reviews, dependent: :destroy
has_many :has_category
has_many :categories, through: :has_category
validates :name, presence: true
end
class Category < ApplicationRecord
has_many :has_category
has_many :courses, through: :has_category
end
class HasCategory < ApplicationRecord
belongs_to :category
belongs_to :course
end
and a simple form to create a new course with different categories using check_box_tag (not sure if using it correctly though)
<%= simple_form_for [#studio, #course] do |f| %>
<%= f.input :name %>
<%= f.input :description %>
<% #categories.each do |category| %>
<%= check_box_tag "course[category_ids][]", category.id, true %>
<%= category.name%>
<% end %>
<%= f.button :submit %>
<% end %>
And all is permitted and created on the courses controller:
def new
#studio = Studio.find(params[:studio_id])
#course = Course.new
#course.studio = #studio
#categories = Category.all
end
def create
#studio = Studio.find(params[:studio_id])
#course = Course.new(course_params)
#course.studio = #studio
#categories = params[:category_ids]
if #course.save
redirect_to course_path(#course)
else
render :new
end
end
def course_params
params.require(:course).permit(:studio_id, :name, :description, :category_ids)
end
With better_errors i know the categories are being requested, here the request info:
"course"=>{"name"=>"Course test", "description"=>"testing", "category_ids"=>["2", "3"]}, "commit"=>"Create Course", "controller"=>"courses", "action"=>"create", "studio_id"=>"16"}
but the categories are not saved on course_params, HasCategory instance or on the Course, i´ve tried with #course.categories = params[:category_ids] and other solutions without success.
How do i save the categories to the courses?
Try the following
Change the strong parameter with category_ids: []
def course_params
params.require(:course).permit(:studio_id, :name, :description, category_ids: [])
end
Comment out this line #categories = params[:category_ids]
Hope it helps
I have three model classes related to each other.
class Student < ActiveRecord::Base
has_many :marks
belongs_to :group
accepts_nested_attributes_for :marks,
reject_if: proc { |attributes| attributes['rate'].blank?},
allow_destroy: true
end
This class describes a student that has many marks and I want to create a Student record along with his marks.
class Mark < ActiveRecord::Base
belongs_to :student, dependent: :destroy
belongs_to :subject
end
Marks are related both to the Subject and a Student.
class Subject < ActiveRecord::Base
belongs_to :group
has_many :marks
end
When I try to create the nested fields of marks in loop labeling them with subject names and passing into in it's subject_id via a loop a problem comes up - only the last nested field of marks is saved correctly, whilst other fields are ignored. Here's my form view code:
<%= form_for([#group, #student]) do |f| %>
<%= f.text_field :student_name %>
<%=f.label 'Student`s name'%><br>
<%= f.text_field :student_surname %>
<%=f.label 'Student`s surname'%><br>
<%=f.check_box :is_payer%>
<%=f.label 'Payer'%>
<%= f.fields_for :marks, #student.marks do |ff|%>
<%#group.subjects.each do |subject| %><br>
<%=ff.label subject.subject_full_name%><br>
<%=ff.text_field :rate %>
<%=ff.hidden_field :subject_id, :value => subject.id%><br>
<%end%>
<% end %>
<%= f.submit 'Add student'%>
<% end %>
Here`s my controller code:
class StudentsController<ApplicationController
before_action :authenticate_admin!
def new
#student = Student.new
#student.marks.build
#group = Group.find(params[:group_id])
#group.student_sort
end
def create
#group = Group.find(params[:group_id])
#student = #group.students.new(student_params)
if #student.save
redirect_to new_group_student_path
flash[:notice] = 'Студента успішно додано!'
else
redirect_to new_group_student_path
flash[:alert] = 'При створенні були деякі помилки!'
end
end
private
def student_params
params.require(:student).permit(:student_name, :student_surname, :is_payer, marks_attributes: [:id, :rate, :subject_id, :_destroy])
end
end
How can I fix it?
#student.marks.build
This line will reserve an object Mark.
If you want multi marks, May be you need something like this in new action :
#group.subjects.each do |subject|
#student.marks.build(:subject=> subject)
end
Hope useful for you.
I have this fields_for in my form:
= f.fields_for :parts do |builder|
= builder.input :body, label: builder.object.another_relation.description, required: false
In the main model I have this validation:
validates_associated :parts
In the parts model I have this validation:
validates :body, presence: true
This works well. When I leave some part empty and try to create the model, the form is re-displayed again with proper error messages.
But in the logs I see that the label is querying the database for each part, so I need to eager load this.
How?
This was my try:
= f.fields_for :parts, f.object.parts.includes(:question_part) do |builder|
= builder.input :body, label: builder.object.another_relation.description, required: false
This eager loads the relationship successfully, but when I leave some field empty and try to create the model, the form is re-displayed with all parts empty and no errors. I think this is normal, as I'm forcing the fields_for to load fields from that collection I'm passing.
Anyone knows how to solve this?
Here's a way to do it.
Imagine these 4 models, related to a question-answers site like StackOverflow, so everyone knows what we are talking about :)
So, a question has N parts, and also N answers. While an answer belongs to a question, and has N parts. Finally, an answer part belongs to a question part.
class Question
has_many :parts, class_name: 'QuestionPart', dependent: :destroy
accepts_nested_attributes_for :parts, allow_destroy: true
has_many :answers, dependent: :destroy
# ...
end
class QuestionPart
belongs_to :question
# ...
end
class Answer
belongs_to :question
belongs_to :user
has_many :parts, class_name: 'AnswerPart', dependent: :destroy
accepts_nested_attributes_for :parts, allow_destroy: true
# ...
end
class AnswerPart
belongs_to :answer
belongs_to :question_part
validates :body, presence: true
# ...
end
Let's take a look at the answer controller:
class AnswersController < ApplicationController
def new
#question = Question.find(params[:question_id)
#answer = #question.answers.new
#question.parts.each { |p| #answer.parts.build(question_part: p) }
end
def edit
#answer = Answer.find(params[:id])
#answer_parts = #answer.parts.includes(:question_part)
end
def create
#question = Question.find(params[:question_id])
#answer = current_user.answers.build(answer_params.merge(question: #question))
if #answer.save
redirect_to #question, notice: 'The answer was successfully created.'
else
render :new
end
end
def update
##answer = Answer.find(params[:id])
#answer = Answer.where(id: params[:id]).includes(parts: [:question_part]).first
if #answer.update(answer_params)
redirect_to #answer.question, notice: 'The answer was successfully updated.'
else
render :edit
end
end
private
def answer_params
params.require(:answer).permit(:body, parts_attributes: [:id, :question_part_id, :body])
end
end
Please notice how I retrieve the answer in the update method. I have commented out the standard way, while I'm retrieving it with a custom query (which should be moved into the Answer model). Here is where I had trouble. If anyone knows a better way, just comment or add a new answer.
Now for the templates:
# new.html.erb
...
<%= form_for [#question, #answer] do |f|
<%= render 'form', f: f, parts: #answer.parts
<% end %>
# edit.html.erb
...
<%= form_for [#answer] do |f|
<%= render 'form', f: f, parts: #answer_parts || #answer.parts
<% end %>
# _form.html.erb
...
<%= f.fields_for :parts, parts do |builder| %>
<%= builder.text_field :body, label: builder.object.question_part.description, required: false
<%= builder.hidden_field :question_part_id %>
<% end %>
Here are my models:
class Project < ActiveRecord::Base
has_many :project_applications
has_many :questions
accepts_nested_attributes_for :questions, :allow_destroy => true, :reject_if => proc { |a| a[:content].blank? }
end
class Question < ActiveRecord::Base
belongs_to :project
has_many :answers
has_many :project_applications, through: :answers
end
class Answer < ActiveRecord::Base
belongs_to :question
belongs_to :project_application
end
class ProjectApplication < ActiveRecord::Base
belongs_to :project
belongs_to :student
has_many :answers
has_many :questions, through: :answers
end
A project is created by an employer, and a student can create a project_application. The project_application should present the questions and then show form fields that correspond to the questions answers. I cannot for the life of me figure out how the form view should look. I need a form_for ProjectApplication that accepts nested attributes for answers. I have the following in my controller:
class ProjectApplicationsController < ApplicationController
def new
#project = Project.find(params[:project_id])
#project_application = ProjectApplication.new
#project_application.project = #project
#project_application.project.questions.each do |question|
#answer = question.answers.build
#answer.project_application = #project_application #this line does not work
puts 'answer' + #answer.inspect.to_s
end
puts 'here are the answers' + #project_application.answers.inspect.to_s
end
end
The problem with this is that the answers are not correctly being associated with project_applications because the project_applications don't have an id yet (because they have not been created) so the association can't happen, so the answer fields are not displayed. Here is the view code (does not work) that I have now:
<%= form_for #project_application, url: project_project_applications_path(#project.id), method: :post, remote: true do |f| %>
<%= f.fields_for :project do |proj| %>
<%= proj.fields_for :questions do |quest| %>
<%= quest.fields_for :answers do |answer| %>
<%= answer.text_area :content %>
<% end %>
<% end %>
<% end %>
<%= f.submit "APPLY" %>
<% end %>
How do I change the view and/or controller to properly display answer fields correctly associated with questions and the project application?
My understanding:
You have projects
Each project has many questions & project_applications
Each question belongs to a project, has many answers through project_applications
For each project, you'd like to create applications with the applicable answers. Here's what I'd do:
Models
class Project < ActiveRecord::Base
has_many :project_applications
has_many :questions
end
class Question < ActiveRecord::Base
belongs_to :project
has_many :answers
has_many :project_applications, through: :answers
end
class Answer < ActiveRecord::Base
belongs_to :question
belongs_to :project_application
end
class ProjectApplication < ActiveRecord::Base
belongs_to :project
belongs_to :student
has_many :answers
has_many :questions, through: :project
accepts_nested_attributes_for :answers
def self.build
application = self.new
application.answers.build
end
end
Controller
#app/controllers/project_applications_controller.rb
def new
#projectapplication = ProjectApplication.build
#project = #projectapplication.project
end
def create
end
private
def application_params
params.require(:project_application).permit(:application, :attributes, :here, answer_attributes: [:content])
end
Views
#app/views/project_applications/new.html.erb
<%= form_for #projectapplication do |f| %>
<%= f.text_field :application_fields %>
<% #project.questions.each do |question| %>
<%= f.fields_for :answers, question do |answer| %>
<%= answer.hidden_field :question_id, question.id %>
<%= answer.text_field :content, placeholder: question.content %>
<% end %>
<% end %>
<% end %>
Process
This works by creating a new project_application, sending answers directly to the answer model. Because answers are directly associated with questions, and projectsthrough questions, you should be able to get it working without passing any more data
This might not work outright, but I'm sure with some tweaking it will deliver the desired result