【问题标题】:RoR: deep clone: how do I also clone models that reference the one I'm cloning? - as in clone models via the has_many relationshipRoR:深度克隆:我如何克隆引用我正在克隆的模型? - 与通过 has_many 关系的克隆模型一样
【发布时间】:2012-02-19 14:47:18
【问题描述】:

我的代码:

  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 => [:account_id, :user_id, :ext_integration_id, :category_id], :except => [:closed_comment_id]})

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

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

【问题讨论】:

    标签: ruby-on-rails ruby 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
    

    这应该会导致新帖子与旧帖子的标签相同,但不会复制 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
    

    希望有帮助:)

    【讨论】:

    • 感谢您帖子中的详细程度和示例!当我们升级到 rails 3 时,我已将此添加到我们的票务跟踪器中。它肯定比我们目前使用的 gem 更具 rails-y 感觉:github.com/DerNalia/deep_cloning(只是扩展 ActiveRecord::Base)
    【解决方案2】:

    这个宝石可以解决问题:

    https://github.com/DerNalia/deep_cloning

    目前拥有所有 deep_cloning gem 中最多的功能。

    使用 Rails 2.3.8 在 1.8.7 上测试

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-09-06
      • 1970-01-01
      • 2020-03-04
      • 2012-10-19
      • 1970-01-01
      • 2017-09-17
      • 1970-01-01
      相关资源
      最近更新 更多