RoR:深度克隆:我如何克隆引用我正在克隆的模型的模型? - 通过has_many关系在克隆模型中

时间:2012-01-26 14:08:52

标签: ruby-on-rails ruby deep-copy

我的代码:

  class  ActiveRecord::Base  
      def clone!(options = {})
        defaults = {:except => [:updated_at, :cerated_at, :id], :shallow => []}
        options = defaults.merge(options)

        skip_attributes = options[:except] or false #attributes not to clone at all
        shallow_attributes = options[:shallow] or false # non-recursivly cloned attributes
        options[:except] << self.class.to_s.foreign_key # add current class to exceptions to prevent infinite loop

        new_model = self.class.new

        self.attributes.each_pair do |attribute, value|
          skip_attribute = (skip_attributes ? skip_attributes.map{|a| a.to_s}.include?(attribute) : false)
          next if skip_attribute

          shallow_copy = (shallow_attributes ? shallow_attributes.map{|s| s.to_s}.include?(attribute) : false)

          if attribute =~ /_id\z/ and (not shallow_copy)
            # assume reference to a different object
            model_table_name = attribute.gsub(/_id\z/, "")
            model_name = model_table_name.camelize

            referenced_object = model_name.constantize.find(value).clone!(options)
            puts attribute.inspect
            puts referenced_object.inspect
            new_model.send("#{attribute}=", referenced_object[:id])
          else
            new_model.send("#{attribute}=", value) 
          end
        end

        new_model.save!
      end
    end

因此,调用该方法的一种方法是:

b = MyObject.find(432).clone!({:shallow =&gt; [:account_id,:user_id,:ext_integration_id,:category_id],:except =&gt; [:closed_comment_id]})

问题是MyObject HAS MANY OtherObjects,因此MyObject本身并不直接引用OtherObjects,因为OtherObjects具有MyObject的外键。

如何找出具有这种关系的模型的名称?

2 个答案:

答案 0 :(得分:2)

我不确定这是否完全适用,因为我不确定我究竟想要复制什么以及你不想复制什么,但是...

对于ActiveRecord 3.2,您可以从Amoeba gem中获得一些好用。

它支持has_onehas_manyhas_and_belongs_to_many关联的简单和自动递归复制,字段预处理以及高度灵活且功能强大的配置DSL,可以应用于模型和苍蝇。

请务必查看Amoeba Documentation,但使用非常简单......

gem install amoeba

或添加

gem 'amoeba'

到您的Gemfile

然后将变形虫块添加到模型中并照常运行dup方法

class Post < ActiveRecord::Base
  has_many :comments
  has_and_belongs_to_many :tags

  amoeba do
    enable
  end
end

class Comment < ActiveRecord::Base
  belongs_to :post
end

class Tag < ActiveRecord::Base
  has_and_belongs_to_many :posts
end

class PostsController < ActionController
  def some_method
    my_post = Post.find(params[:id])
    new_post = my_post.dup
    new_post.save
  end
end

您的新帖子应该包含最初与之关联的所有标记,并且所有评论也应该重复。您可以通过DSL禁用各种记录的复制,您可以在文档中阅读这些记录,但是,例如,如果您想保留标记,而不是注释,则可以执行以下操作:

class Post < ActiveRecord::Base
  has_many :comments
  has_and_belongs_to_many :tags

  amoeba do
    include_field :comments
  end
end

这应该会导致新帖子与旧帖子重新关联,但不会重复评论。

如果您启用了Amoeba,Amoeba也会自动递归到子记录中

class Post < ActiveRecord::Base
  has_many :comments

  amoeba do
    enable
  end
end

class Comment < ActiveRecord::Base
  belongs_to :post
  has_many :ratings

  amoeba do
    enable
  end
end

class Rating < ActiveRecord::Base
  belongs_to :comment
end

希望有所帮助:)

答案 1 :(得分:0)

这个宝石可以解决这个问题:

https://github.com/DerNalia/deep_cloning

目前拥有任何深度克隆宝石的最多功能。

使用Rails 2.3.8在1.8.7上测试