Forum: Ruby on Rails Creating multiple child objects from the same form

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
A8d50e9e6ffc2670d0db1de5cb72ff83?d=identicon&s=25 Xavier Lange (derdewey)
on 2006-01-18 02:21
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' %>

<!--[form:question]-->
<p><label for="question_question">Question</label><br/>
<%= text_area 'question', 'question', {:cols=>"80", :rows=>"5"}  %></p>

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

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

<!--[eoform:question]-->
18d8b0c948dd7b3d39c9667cfd4e817c?d=identicon&s=25 Andrew Stone (Guest)
on 2006-01-18 02:39
(Received via mailing list)
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.
A8d50e9e6ffc2670d0db1de5cb72ff83?d=identicon&s=25 Xavier Lange (derdewey)
on 2006-01-18 03:01
Andrew Stone 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' %>

<!--[form:question]-->
<p><label for="question_question">Question</label><br/>
<%= text_area 'question', 'question', {:cols=>"80", :rows=>"5"}  %></p>

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

<p><label for="question_answer">Answer</label><br/>
<%= text_area "question_answer_2", 'answer[]', {:cols=>"80", :rows=>"5"}
%></p>

<!--[eoform:question]-->
18d8b0c948dd7b3d39c9667cfd4e817c?d=identicon&s=25 Andrew Stone (Guest)
on 2006-01-18 03:10
(Received via mailing list)
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]
A8d50e9e6ffc2670d0db1de5cb72ff83?d=identicon&s=25 Xavier Lange (derdewey)
on 2006-01-18 03:12
Andrew Stone 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?
18d8b0c948dd7b3d39c9667cfd4e817c?d=identicon&s=25 Andrew Stone (Guest)
on 2006-01-18 03:16
(Received via mailing list)
you could do something like:

@params.each{|key,value|
  if key.downcase =~ /^question_answer/
    @question.answers << Answer.new(:answer => value)
  end
}
9b7647d55df4584d7031130915556040?d=identicon&s=25 Will Briggs (Guest)
on 2006-01-18 03:22
(Received via mailing list)
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
18d8b0c948dd7b3d39c9667cfd4e817c?d=identicon&s=25 Andrew Stone (Guest)
on 2006-01-18 03:31
(Received via mailing list)
Thanks Will, I did not know that. :)
A8d50e9e6ffc2670d0db1de5cb72ff83?d=identicon&s=25 Xavier Lange (derdewey)
on 2006-01-18 03:33
Andrew Stone 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
18d8b0c948dd7b3d39c9667cfd4e817c?d=identicon&s=25 Andrew Stone (Guest)
on 2006-01-18 03:49
(Received via mailing list)
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.
9b7647d55df4584d7031130915556040?d=identicon&s=25 Will Briggs (Guest)
on 2006-01-18 03:58
(Received via mailing list)
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
A8d50e9e6ffc2670d0db1de5cb72ff83?d=identicon&s=25 Xavier Lange (derdewey)
on 2006-01-18 04:02
Andrew Stone 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!
18d8b0c948dd7b3d39c9667cfd4e817c?d=identicon&s=25 Andrew Stone (Guest)
on 2006-01-18 04:04
(Received via mailing list)
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... :)
A8d50e9e6ffc2670d0db1de5cb72ff83?d=identicon&s=25 Xavier Lange (derdewey)
on 2006-01-18 04:07
Will Briggs 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".
A8d50e9e6ffc2670d0db1de5cb72ff83?d=identicon&s=25 Xavier Lange (derdewey)
on 2006-01-18 04:15
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
      }
9b7647d55df4584d7031130915556040?d=identicon&s=25 Will Briggs (Guest)
on 2006-01-18 04:19
(Received via mailing list)
Ok try doing this:

<%= text_area "1", 'answer', :prefix => 'question_answer', {:cols =>
"80", :rows => "5"} %>

-Will
18d8b0c948dd7b3d39c9667cfd4e817c?d=identicon&s=25 Andrew Stone (Guest)
on 2006-01-18 04:22
(Received via mailing list)
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.
A8d50e9e6ffc2670d0db1de5cb72ff83?d=identicon&s=25 Xavier Lange (derdewey)
on 2006-01-18 04:27
Andrew Stone 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!
Fcdafe495dccc04d27f6dbbcdf54aecc?d=identicon&s=25 Woei Shyang (generik)
on 2006-01-18 05:01
Sorry to barge into your thread :)

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.
A8d50e9e6ffc2670d0db1de5cb72ff83?d=identicon&s=25 Xavier Lange (derdewey)
on 2006-01-18 05:07
Woei Shyang wrote:
> Sorry to barge into your thread :)
>
> 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.

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 :)

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?
Fcdafe495dccc04d27f6dbbcdf54aecc?d=identicon&s=25 Woei Shyang (generik)
on 2006-01-18 05:18
Xavier Lange 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 :)
>
> 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. :/

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*
A8d50e9e6ffc2670d0db1de5cb72ff83?d=identicon&s=25 Xavier Lange (derdewey)
on 2006-01-18 06:12
Woei Shyang wrote:
> I hope there is a better way. :/
>
> 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*

Hey RoR people, we're talking to you!  Haha.

Well, I think I'll make the rest of this application and come back to
this later.  Maybe I'll have learned a bit more so it doesn't feel quite
as hacked together.  Thanks for the help everyone!
Fcdafe495dccc04d27f6dbbcdf54aecc?d=identicon&s=25 Woei Shyang (generik)
on 2006-01-18 06:47
Xavier Lange wrote:
> Hey RoR people, we're talking to you!  Haha.
>
> Well, I think I'll make the rest of this application and come back to
> this later.  Maybe I'll have learned a bit more so it doesn't feel quite
> as hacked together.  Thanks for the help everyone!

A thought did pop into my head just now.. maybe we can shift the
validation code into the controller, but it does seem like quite a waste
considering the child class has its own validation as well.

Just a suggestion though :)
This topic is locked and can not be replied to.