【问题标题】:Cloning a record in rails, is it possible to clone associations and deep copy?在rails中克隆记录,是否可以克隆关联和深拷贝?
【发布时间】:2011-08-24 00:24:56
【问题描述】:

我正在 .clone - 在 rails 中记录一条记录...

  new_blerg = Blerg.find(1).clone

这条记录有很多关联,而且这些关联甚至还有关联。

有没有办法深度复制记录并克隆它,以便它也与所有这些关联一起克隆?

【问题讨论】:

    标签: ruby-on-rails activerecord duplicates clone deep-copy


    【解决方案1】:

    您可以从Amoeba gem for ActiveRecord 3.2 中获得一些好处。

    它支持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
    

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

    class Post < ActiveRecord::Base
      has_many :comments
      has_and_belongs_to_many :tags
    
      amoeba do
        include_field :comments
      end
    end
    

    或使用独占语法

    class Post < ActiveRecord::Base
      has_many :comments
      has_and_belongs_to_many :tags
    
      amoeba do
        exclude_field :comments
      end
    end
    

    或通过指定要识别(并因此复制)的字段类型

    class Post < ActiveRecord::Base
      has_many :comments
      has_and_belongs_to_many :tags
    
      amoeba do
        recognize :has_and_belongs_to_many
      end
    end
    

    这些不同的选项中的每一个都应该导致新帖子与旧帖子的标签相同,但不会复制 cmets。

    如果您启用它们,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
    

    你也可以在字段前加上一些额外的数据来表示唯一性

    class Post < ActiveRecord::Base
      has_many :comments
    
      amoeba do
        enable
        prepend :title => "Copy of "
      end
    end
    

    除了前置之外,您还可以在给定字段上附加或运行正则表达式

    享受吧! :)

    【讨论】:

    • 哇,这颗宝石真是太棒了。我不得不推出自己的复制系统,但它无法正常工作,但您的 gem 工作得非常好。
    • 示例中的 .dup 是否应为文档中定义的“new_post = my_post.amoeba_dup”?
    • @kibaekr 从 README 历史记录中我发现“截至 2012 年 12 月 11 日,Amoeba 不再覆盖内置的 ActiveRecord::Base#dup 方法,而是实现了自己的名为 amoeba_dup 的方法...”跨度>
    【解决方案2】:

    同样,这个 gem 似乎运行良好:https://github.com/moiristo/deep_cloneable,并且非常易于使用。

    只是

    gem ‘deep_cloneable’, ‘~&gt; 1.4.0’

    然后:

    pirate.deep_clone :include =&gt; :mateys

    【讨论】:

    • 我发现这比 amoeba 更容易实现——模型中不需要声明。
    • 是的,这比 ameoba 的开销要少得多,不需要学习任何声明或 dsl。我认为它也更“铁轨”。灵活性也很稳固。 Rails 应该将其添加为 AR 方法。
    • 新宠宝石。
    【解决方案3】:

    您需要编写自己的 clone_with_associations 方法,该方法会遍历一组特定的关联。从理论上讲,您可以编写一些使用 reflect_on_all_associations 的通用程序,但您需要对关联的对象执行相同的操作,这将不可避免地最终创建一个生成无限量记录的循环。

    所以,写你自己的。像

      #in Blerg
      has_many :foos
      has_many :bars #bars also have many chickens which we want to copy over as well
      def clone_with_associations
        new_blerg = self.dup
        new_blerg.save
        #simple association
        new_blerg.foos = self.foos
        #two-level association 
        self.bars.each do |bar|
          new_bar = bar.clone
          new_bar.save
          new_bar.chickens = bar.chickens 
          new_blerg.bars << bar
        end
        new_blerg
      end
    

    现在你可以做

    @new_blerg = Blerg.find(1).clone_with_associations
    

    【讨论】:

    • 你会得到一个损坏的原始对象,因为这个 new_blerg.foos = self.foos 窃取了你的关联。您还需要克隆它们。
    • 这是最好的答案。在我看来,在这种情况下滚动你自己的,比使用宝石更清洁、更容易、更不神奇。
    • RocketR - 好点子。我想我假设 .foos 关系是“拥有并属于许多人”,在这种情况下它会很好,但如果 foo belongs_to blerg 那么是的,它会改变关联的 foos。
    • 谢谢@MaxWilliams !我使用了这种方法。
    • 为什么每次复制后都要保存?你能在最后保存吗?
    猜你喜欢
    • 2011-09-05
    • 1970-01-01
    • 2011-03-24
    • 1970-01-01
    • 2010-11-30
    • 1970-01-01
    • 2012-10-17
    • 2011-09-16
    • 1970-01-01
    相关资源
    最近更新 更多