Forum: Ruby on Rails ActiveRecord: moving all children to a new parent

0fa73332c8e4a3b06ea439fd3f034322?d=identicon&s=25 Ronald Fischer (rovf)
on 2014-07-17 13:27
(Workaround given at the end, but better suggestions appreciated)

I have a models Parent and Child, an the following association:

Parent
  has_many :children, dependent: :destroy

Child
  belongs_to :parent

Further, I have two Parent instances:

  pfrom = Parent.find_by_id(from_id)
  pto = Parent.find_by_id(to_id)

My goal is to transfer all children from pto to pfrom, and then delete
pto.

The first part seems to be easy:

  pfrom.children.each { |ch| ch.update_attributes!(parent_id: pto.id }

If I run *only* this code, I can see that pto indeed contains now the
children formerly belonging to pfrom, and iterating over pfrom shows
that there are no children.

HOWEVER, if I add the following line:

  pfrom.destroy

I can see (from the SQL statements which are issued by this call), that
all the former pfrom children are deleted!

It somehow seems as if this information has been "cached". Could this be
the case? How then would I correctly implement the "move".

====================================

WORKAROUND:

I found that if I destroy pfrom like this:

  Parent.find_by_id(pfrom.id).destroy

only the dictionary is deleted. Hence, the pfrom variable still
remembered their children (even though pfrom.children.count had returned
zero).
A47e0a6beeb9d048ff054fc1c3a97418?d=identicon&s=25 Walter Davis (walterdavis)
on 2014-07-17 15:16
(Received via mailing list)
On Jul 17, 2014, at 7:27 AM, Ronald Fischer wrote:

>  pfrom = Parent.find_by_id(from_id)
> children formerly belonging to pfrom, and iterating over pfrom shows
> the case? How then would I correctly implement the "move".
Reload the parent that you wish to destroy before you destroy it. Also,
maybe it would be enough to set the "dead" parent's children array to
[]. While the key is stored only on one side, when the parent record is
initialized, its children are instantiated in memory. You are correct,
they are cached.

Walter
0fa73332c8e4a3b06ea439fd3f034322?d=identicon&s=25 Ronald Fischer (rovf)
on 2014-07-18 08:24
Walter Davis wrote in post #1152668:
> On Jul 17, 2014, at 7:27 AM, Ronald Fischer wrote:
>>  pfrom = Parent.find_by_id(from_id)
>> children formerly belonging to pfrom, and iterating over pfrom shows
>> the case? How then would I correctly implement the "move".
> Reload the parent that you wish to destroy before you destroy it.

I was not aware of the reload method! Thank you for pointing this out.
So this would be

  pfrom.reload.destroy

> Also,
> maybe it would be enough to set the "dead" parent's children array to
> [].

Interesting idea. I think, 'reload' is nicer, because it is more likely
that this part of the interface won't change when a new version of Rails
is coming. Changing the children-array looks a bit like a hack to me (we
need to know that they are stored in an array). But still I'm curious:
How do I explicitly manipulate the childrens array? I didn't find a
suitable method in the Active Record docs, and I don't expect that
something like

  pfrom.children=[]

would do it.

Ronald
6883e5ef03484d4fcef507d7b4f1d243?d=identicon&s=25 Matt Jones (Guest)
on 2014-07-18 15:17
(Received via mailing list)
On Friday, 18 July 2014 02:24:59 UTC-4, Ruby-Forum.com User wrote:
>
> How do I explicitly manipulate the childrens array? I didn't find a
> suitable method in the Active Record docs, and I don't expect that
> something like
>
>   pfrom.children=[]
>
> would do it.
>
>
That's actually *exactly* what will do it. :)

Running that will do a single UPDATE query to set all the involved
parent_id columns to NULL. So one way to implement the swap is:

saved_children = pfrom.children.to_a
pfrom.children = []
pto.children = saved_children

NOTE: this won't work if there are children on pto already. For that,
try
`pto.children = pto.children + saved_children`.

--Matt Jones
0fa73332c8e4a3b06ea439fd3f034322?d=identicon&s=25 Ronald Fischer (rovf)
on 2014-07-18 19:35
I am stunned!!!!!

Every day I like Rails more....

Thanks a lot!

Ronald
0fa73332c8e4a3b06ea439fd3f034322?d=identicon&s=25 Ronald Fischer (rovf)
on 2014-07-23 14:22
Matt Jones wrote in post #1152815:
> On Friday, 18 July 2014 02:24:59 UTC-4, Ruby-Forum.com User wrote:
> saved_children = pfrom.children.to_a
> pfrom.children = []
> pto.children = pto.children + saved_children.

I finally found the time to rewrite this part of my application
according to this suggestion, but I now get an error "can't modify
frozen Hash", when I try to add the saved children. The actual code
which I am using is here:

# tempdict is "pfrom" and targetdict is "pto"
       targetdict=Dict.find_by_id(....)
      # creating and saving a tempdict together with several children,
i.e. cards
      tempdict=Dict.new(....)
      tempdict.save!
      # Code for creating and adding the children omitted for brevity
      ....
      cards_to_add=tempdict.cards.to_a
      tempdict.cards=[]
      targetdict.cards += cards_to_add

I find it strange that I get the error on the last line. The error
message is usually an indication that I am trying to save something
which has been deleted already, but in this case, no deletion had been
done on 'targetdict' before.

Any idea, where this error could come from?
81b61875e41eaa58887543635d556fca?d=identicon&s=25 Frederick Cheung (Guest)
on 2014-07-23 15:59
(Received via mailing list)
>
> saved_children = pfrom.children.to_a
> pfrom.children = []
> pto.children = saved_children
>
>
This will depend on what you have set the :dependant option on the
association to - it would do this if you have set the option to nullify
(the default), but it would destroy the children if it was :destroy or
:delete_all


Fred
0fa73332c8e4a3b06ea439fd3f034322?d=identicon&s=25 Ronald Fischer (rovf)
on 2014-07-24 08:54
Ah, I have set it to :destroy!

But why do I get the error when I assign to pto.children? I would expect
such an error then when pfrom is deleted.

Ronald
6883e5ef03484d4fcef507d7b4f1d243?d=identicon&s=25 Matt Jones (Guest)
on 2014-07-24 21:21
(Received via mailing list)
On Wednesday, 23 July 2014 08:23:25 UTC-4, Ruby-Forum.com User wrote:
> which I am using is here:
>       tempdict.cards=[]
>       targetdict.cards += cards_to_add
>
> I find it strange that I get the error on the last line. The error
> message is usually an indication that I am trying to save something
> which has been deleted already, but in this case, no deletion had been
> done on 'targetdict' before.
>
> Any idea, where this error could come from?
>
>
You tell me; you're the one who can see the stack trace. :)

Maybe something that it's modifying has been destroyed?

--Matt Jones
Please log in before posting. Registration is free and takes only a minute.
Existing account

NEW: Do you have a Google/GoogleMail, Yahoo or Facebook account? No registration required!
Log in with Google account | Log in with Yahoo account | Log in with Facebook account
No account? Register here.