Creating multiple child objects from the same form


#1

I’ve been digging into rails, but this one question has been a two day
stumper. I’ve got a question/answer model that I’m trying to work out.
Each question has multiple answers (they’re more like choices, it
doesn’t matter whether they’re right or wrong), and I’d like to have the
question creation form also have multiple fields in which to put the
answer choices. But with my code, I can only do one successful child
creation. The log only shows that one answer is being passed in.

Here’s the code as of right now:

############ models
class Answer < ActiveRecord::Base
belongs_to :question
validates_presence_of :answer, :question_id
end

class Question < ActiveRecord::Base
validates_presence_of :question
has_many :answers
end

############ question controller
def create
@question = Question.new(params[:question])

if @question.save
  logger.debug(@question.inspect)
  params[:question_answer].each_value do |answer|
    @question.answers << Answer.new(:answer => answer)
  end

  flash[:notice] = 'Question was successfully created.'
  redirect_to :action => 'list'
else
  render :action => 'new'
end

end

############# question’s _form
<%= error_messages_for ‘question’ %>

Question
<%= text_area 'question', 'question', {:cols=>"80", :rows=>"5"} %>

Answer
<%= text_area "question_answer", 'answer[]', {:cols=>"80", :rows=>"5"} %>

Answer
<%= text_area "question_answer", 'answer[]', {:cols=>"80", :rows=>"5"} %>


#2

The reason you’re only getting one answer field back on the post is
because
the names of the text areas are the same. Change the second one to
name=“question_answer_2” and you will see it in the params array.


#3

Andrew S. wrote:

The reason you’re only getting one answer field back on the post is
because
the names of the text areas are the same. Change the second one to
name=“question_answer_2” and you will see it in the params array.

Thanks for the tip, I’ve tried what you just offered and it still only
put in the first answer, the second answer was not input.

################ New _form.rhtml

<%= error_messages_for ‘question’ %>

Question
<%= text_area 'question', 'question', {:cols=>"80", :rows=>"5"} %>

Answer
<%= text_area "question_answer", 'answer[]', {:cols=>"80", :rows=>"5"} %>

Answer
<%= text_area "question_answer_2", 'answer[]', {:cols=>"80", :rows=>"5"} %>


#4

You’re not going to be able to get the multiple values with:

params[:question_answer].each_value do |answer|

you’re going to need to explicitly get each question_answer:

e.g.
a1 = params[:question_answer_1]
a2 = params[:question_answer_2]


#5

Andrew S. wrote:

You’re not going to be able to get the multiple values with:

params[:question_answer].each_value do |answer|

you’re going to need to explicitly get each question_answer:

e.g.
a1 = params[:question_answer_1]
a2 = params[:question_answer_2]

Isn’t there a better way? Because there will be an arbitrary number of
answers, so hard coding that in would not work. There isn’t anyway to
pass all the values into a hash from the form?


#6

you could do something like:

@params.each{|key,value|
if key.downcase =~ /^question_answer/
@question.answers << Answer.new(:answer => value)
end
}


#7

Thanks Will, I did not know that. :slight_smile:


#8

If you name each field “question_answer[]”, the value
params[:question_answer] will be an array of the values entered.

You should probably add the HTML option :name => “question_answer[]”
to each tag. Let me know how that works for you.

Andrew’s solution of using regexes to parse all the params is also
workable, but I think storing them all into an array to begin with is
a little more elegant.

-Will


#9

Andrew S. wrote:

you could do something like:

@params.each{|key,value|
if key.downcase =~ /^question_answer/
@question.answers << Answer.new(:answer => value)
end
}

Wow, that’s a slick way of doing it!

Now it inserts all the values at the same time, only catch is that it
includes the data type. How can I tell what else is inside of the
‘value’ object?

— !ruby/hash:HashWithIndifferentAccess answer: W


#10

I used the regex in the past for my pages. For instance, I may have
checkboxes for departments on a company division edit page. I would
append
the department_id to the name of the check box (department_1), then flip
through with the regex, split the key to get the id and add departments
to
the division.

By using the array as you pointed out, I would have to query to find the
department by the name or description (or whatever) that was shown to
the
user.

Or, am I missing something completely obvious.


#11

You could also do fields with names:

question_answer[answer1], question_answer[answer2], etc…

This makes params[:question_answer] a hash w/ “answer1” as the key and
the value in the “answer1” field as the value, “answer2” as the key
and the value in “answer2” as the field value… does that help at
all?

-Will


#12

Andrew S. wrote:

I used the regex in the past for my pages. For instance, I may have
checkboxes for departments on a company division edit page. I would
append
the department_id to the name of the check box (department_1), then flip
through with the regex, split the key to get the id and add departments
to
the division.

By using the array as you pointed out, I would have to query to find the
department by the name or description (or whatever) that was shown to
the
user.

Or, am I missing something completely obvious.

I had the idea that you could pass in values using a [] at the end of
the variable name and it would then let you treat the variable as a
hash. Guess not. Which is a shame, because that could be very handy!


#13

Yeah, that makes sense. Not that I was having trouble with the regex,
but I
always like to find cleaner implementations. Especially since, compared
to
JAVA, I’m really new to Ruby.

thanks again… :slight_smile:


#14

Will B. wrote:

You could also do fields with names:

question_answer[answer1], question_answer[answer2], etc…

This makes params[:question_answer] a hash w/ “answer1” as the key and
the value in the “answer1” field as the value, “answer2” as the key
and the value in “answer2” as the field value… does that help at
all?

-Will

Yea, I had tried that and rails spit out that it wasn’t allowed as an
instance name. This is what I just tried now: <%= text_area
“question_answer[1]”, ‘answer’, {:cols=>“80”, :rows=>“5”} %> and rails
said “`@question_answer[1]’ is not allowed as an instance variable
name”.


#15

Ok try doing this:

<%= text_area “1”, ‘answer’, :prefix => ‘question_answer’, {:cols =>
“80”, :rows => “5”} %>

-Will


#16

Any ideas on how to deal with “— !ruby/hash:HashWithIndifferentAccess
answer: WHAT” being inserted instead of just “WHAT”?

I tried @question.answers << Answer.new(:answer => value[answer]) which
threw an error about being the incorrect method. Then I tried
@question.answers << Answer.new(:answer => value.type)” so it would put
into the table what kind of object it was, but then ruby said that it
couldn’t dump anonymous class Class.

What’s a good way to see what the variables are and what they contain?

################### what’s in the controller
@params.each{|key,value|
if key.downcase =~ /^question_answer/
@question.answers << Answer.new(:answer => value)
end
}


#17

The value is from |key,value| is just going to be a string.

You can use

value.inspect
value.class
or just: value
in a log statement to see different attributes of the object. of
course,
inspect varies on the output depending on what type of object it is.


#18

Sorry to barge into your thread :slight_smile:

However one thing about this approach is that supposing one of your
answers fail validation, you will only get a rather cryptic error
message like “Answer is invalid”, with no other explanatory text or any
text fields highlighted (using the default scaffolds that is).

This will happen even if you did provide the appropriate error messages
in your Answers class! Is there any way to sort of “bubble” the error
messages from the child object up to the main form?

Thanks.


#19

Andrew S. wrote:

The value is from |key,value| is just going to be a string.

You can use

value.inspect
value.class
or just: value
in a log statement to see different attributes of the object. of
course,
inspect varies on the output depending on what type of object it is.

Ah, so type wasn’t working but inspect does the trick! Then I figured
out that it needed the string “answer” as the key. So with
Answer.new(:answer => value[“answer”]) my little question/answer system
is in good shape!


#20

Xavier L. wrote:

This solution isn’t as elegant as I at first thought. I’m trying to get
the edit method to work, and it seems like it’s repeating what I did for
the create method. And I thought ruby was all about DRY :slight_smile:

The model is pretty simple, but I don’t know how it will react when it
fails validation. There has to be a better way, right?

I hope there is a better way. :confused:

Surely there is someway to hack the _form partial into doing something
useful for objects that has_many instances of a child subclass… right?

crickets chirp