Jack R-G
Jack R-G

Reputation: 1906

ActiveRecord Move All Children to Another Record

I have a table with five different has-many relationships to other tables. Before deleting a record, I need to move all of its children to another record. To accomplish this I employ the following code:

self.class.reflect_on_all_associations.select {|assoc| assoc.macro == :has_many }.each do |assoc|
  target.send(assoc.name) << self.send(assoc.name)
end

Basically, this comes down to target.child-association << self.child-association. But this does not work correctly, deleting all the children from "self" and adding a single child to "target" with all fields except the associated field null. I think this is because "<<" expects a record and I'm passing an array of records. I see that "<<" also accepts a list of arguments, so I think I need something like target.send(assoc.name) << *self.send(assoc. name) (note the splat operator), but I can't figure out a valid syntax for doing this. So my questions are two:

  1. How do you "splat" an arbitrary array for input to "<<"?
  2. Is that what is needed to make child reassignment work correctly?

Upvotes: 1

Views: 1034

Answers (1)

Jack R-G
Jack R-G

Reputation: 1906

Ok, here are the answers:

1) target.send(assoc.name).send(:<<, *(self.send(assoc.name)))

2) The original technique is inefficient, generating 'n' SQL Updates, each one updating one child record. The more efficient technique is self.send(assoc.name).update_all(parent_id: target.id). It requires guilty knowledge of how the linking is accomplished (in my case and most cases, a single foreign key field) which I suppose could be reflected from the association).

Upvotes: 2

Related Questions