Reputation: 133
I have a survey app, basically along the lines of Railscast 196, but with one snag: where the Railscast has one Question
class, which has_many :answers
, I have several:
Question (self.abstract_class = true)
BasicQuestion < Question
MultipleChoiceQuestion < Question
To make this work I had to override the questions
getter in Survey
, which seems a bit of a kludge but not too bad (is there a standard way to do this?):
Survey.rb
has_many :questions
accepts_nested_attributes_for :questions
def questions # simplified a bit for brevity
questions = []
[BasicQuestion, LikertQuestion, MultipleChoiceQuestion].each do |model|
questions += model.where(:survey_id => self.id)
end
questions
end
Survey_Controller.rb
def survey_params
params.require(:survey).permit(:name, :questions_attributes => [:id, :name])
end
So far, so good. The problem is this:
Again from the Railscast, I have this in surveys/edit.html.erb
:
surveys/edit.html.erb
<%= f.fields_for :questions do |builder| %>
<%= render 'edit_question_fields', f: builder %>
<% end %>
However, this returns a hash of the form:
{ "survey" => { "name" => "Howard", questions_attributes => { "id" => "1", "name" => "Vince" }}}
Rails gives me an error: ActiveRecord::StatementInvalid (Could not find table '')
--
presumably, because there is no Questions
table (it's an abstract class).
So, how do I fix it? Without abandoning nested_attributes
or inheritance entirely, I can think of four ways:
Question
being an abstract class), include the _type
field in the params hash, and go from there.Let Survey
deal with each question type separately:
Survey.rb
has_many :basic_questions
accepts_nested_attributes_for :basic_questions
has_many :multiple_choice_questions
accepts_nested_attributes_for :multiple_choice_questions
def questions
# same as before, still comes in handy
end
surveys/edit.html.erb
<% @survey.questions.each do |question| %>
<%= f.fields_for question do |builder| %>
<%= render 'edit_question_fields', f: builder %>
<% end %>
<% end %>`
This almost works, except that now my hash looks like this:
{ "survey" => { "name" => "Howard", "basic_question" => { "id" => "1", "name" => "Vince" }, "multiple_choice_question" => { "id" => "1", "name" => "Naboo" }}}
I need the questions indexed by, e.g., "basic_questions_attributes"
instead of "basic_question"
-- anyone know how to do this?
Override questions_attributes=
in Survey.rb to sort it all out.
QuestionsFormBuilder
object to handle everything, along the lines of "Rails nested attributes form for polymorphic/single table inheritance associations".Obviously a primary concern is being able to drop in new Question
subclasses later (or change the behavior of existing ones) with a minimum of hassle.
At the moment I'm inclined to go with option #3, as it seems simplest and most elegant, however, I'm not sure I'm not missing some better way to do this. (Or somehow screwing up the Question
subclassing implementation.) Does anyone have any better ideas or more Rails-like ways of getting this to work?!
Upvotes: 4
Views: 1001
Reputation: 2926
Take a look at using a form object to encapsulate the logic and creation of the questions? http://railscasts.com/episodes/416-form-objects
I would also look at using STI so that your Survey.rb doesn't need to redefine questions
Upvotes: 1