Paul
Paul

Reputation: 1031

ActiveRecord: Can I copy associations?

Is there a way to copy the associations of one model to another...

template_model = MyModel.find(id)
new_model = template_model.clone
new_model.children << template_model.children # I want to *copy* children

...such that I copy the children from the template to the new model? (In fact this code moves children from the template to the new model).

I know I can do it manually be looping, but is there are more succinct way?

Thanks

Upvotes: 20

Views: 8066

Answers (4)

Artur INTECH
Artur INTECH

Reputation: 7366

#dup should be used instead of #clone, since attributes are not copied in case of the latter.

module Promotion
  class Banner
    has_many :localizations
    has_many :images

    def deep_dup!
      duplicate = dup
      duplicate.save

      duplicate.localizations = localizations.collect { |localization| localization.dup }
      duplicate.images = images.collect { |image| image.dup }

      duplicate
    end
  end
end

http://api.rubyonrails.org/classes/ActiveRecord/Core.html#method-i-clone http://api.rubyonrails.org/classes/ActiveRecord/Core.html#method-i-dup

Upvotes: 2

Andrey Yasinishyn
Andrey Yasinishyn

Reputation: 1861

Add this some where is under /lib. For example clone_deep.rb.

module CloneDeep
  def clone_deep
    kopy = clone
    self.class.reflect_on_all_associations.each do |association|
      next if association.macro == :belongs_to
      cloned_object = case association.macro
                        when :has_many
                          self.send(association.name).collect { |item| item.clone_deep }
                        when :has_one
                          self.send(association.name) && self.send(association.name).clone_deep
                        else
                          clone
                      end
      kopy.send("#{association.name}=", cloned_object)
    end
    return kopy
  end
end

Create new initializer under config/initializers/ folder. Inside this file paste

ActiveRecord::Base.send(:include, CloneDeep)

Now you be able to clone model with all it's has_one and hos_many associations.

cloned_person = person.clone_deep
cloned_person.save

Upvotes: 6

Jimmy Stenke
Jimmy Stenke

Reputation: 11220

Well, it's not really a copy.

But you could do

new_model.child_ids = template_model.child_ids

Upvotes: 0

MarkusQ
MarkusQ

Reputation: 21950

The problem is that you are cloning the template, but not cloning it's children. Try something like:

template_model = MyModel.find(id)
new_model = template_model.clone
new_model.children << template_model.children.collect { |child| child.clone }

Upvotes: 30

Related Questions