Assigning has_many child in parent creation question


#1

If I have…

class Parent
has_many :children
end

class Child
belongs_to :parent
end

…then…

  • Assigning a new child during parent’s creation saves the child
    record with the parent’s id.
    child = Child.new
    Parent.create(:children => [child])

    Results in child being associated with parent

  • Assigning an existing child after a parent’s creation saves the child
    record with the parent’s id.
    child = Child.new
    parent = Parent.new
    parent.children << child

    Results in child being associated with parent

…however…

  • Assigning an existing child during parent’s creation does not save
    the child’s record with the parent’s id.
    child = Child.create
    Parent.create(:children => [Child.new])

    Child is not associated with parent

This seems slightly counter-intuitive; unless I am missing something
obvious. Any thoughts?

Chris


#2

Sorry, the last example is incorrect - I should be using the new child
in my parent construction

child = Child.create
Parent.create(:children => child)

Chris

Chris R. wrote:

If I have…

class Parent
has_many :children
end

class Child
belongs_to :parent
end

…then…

  • Assigning a new child during parent’s creation saves the child
    record with the parent’s id.
    child = Child.new
    Parent.create(:children => [child])

    Results in child being associated with parent

  • Assigning an existing child after a parent’s creation saves the child
    record with the parent’s id.
    child = Child.new
    parent = Parent.new
    parent.children << child

    Results in child being associated with parent

…however…

  • Assigning an existing child during parent’s creation does not save
    the child’s record with the parent’s id.
    child = Child.create
    Parent.create(:children => [Child.new])

    Child is not associated with parent

This seems slightly counter-intuitive; unless I am missing something
obvious. Any thoughts?

Chris


#3

In your final example the “child = Child.create” line is irrelevant,
right?
“child” is never used. What happens when you do use it? That should
definitely work.

  • Byron

#4

Try this…

Parent.children << Child.new

See what happens…


#5

I probably should have been slightly more explicit in my initial
example.

What you suggest works fine. The reason that your method works is the
basis for my confusion.

With an unsaved child object, both methods (as well as
update_attribute/s) work fine.

With a previously saved child object, only the assignment on parent
creation doesn’t work - all other methods do. This is what seems
counter-intuitive to me.

Chris


#6

Err, apparently I’m more stoopid than I realised. This should of course
be

child = Child.create
Parent.create(:children => [child])

Regardless of my ineptitude, this is the example that does not assign
the child to the parent (i.e. child.parent_id is null in database).

Chris

Chris R. wrote:

Sorry, the last example is incorrect - I should be using the new child
in my parent construction

child = Child.create
Parent.create(:children => child)

Chris


#7

The initial problem with the your code (I believe) is that while you
were assigning the parent/child relationship you weren’t saving the
child again once you did so. As far as I know, the only method that
will actually save/create the child, or related objects, is the <<
operator.

In other words, if you set the parent_id explicitly then you need to
call child.save. Try adding that in your original code after the call
to the parent object.

-Nick


#8

With a previously saved child object, only the assignment on parent
creation doesn’t work - all other methods do. This is what seems
counter-intuitive to me.

The Agile book seems to indicate something like
child.create_parent(attributes={})

Tony


#9

Nick S. wrote:

The initial problem with the your code (I believe) is that while you
were assigning the parent/child relationship you weren’t saving the
child again once you did so. As far as I know, the only method that
will actually save/create the child, or related objects, is the <<
operator.

In other words, if you set the parent_id explicitly then you need to
call child.save. Try adding that in your original code after the call
to the parent object.

-Nick

Thanks for all the pointers folks.

Just for clarity; I already knew how to achieve what I required. I
guess my question was more about the principle of least surprise. For
each method available to ‘join’ a parent and child; all work whether or
not the child is saved; except the parent creation method. This just
surprised me is all.

Thanks for the input,

Chris