ActiveRecord:如何克隆嵌套关联?

我目前正在克隆这样的单级关联:

class Survey < ActiveRecord::Base def duplicate new_template = self.clone new_template.questions << self.questions.collect { |question| question.clone } new_template.save end end 

因此, Survey克隆然后克隆与该调查相关的Questions 。 精细。 这非常有效。

但我遇到的问题是每个问题都有很多问题。 所以Survey has_many Questions which has_many Answers

我无法弄清楚如何正确克隆答案。 我试过这个:

 def duplicate new_template = self.clone self.questions.each do |question| new_question = question.clone new_question.save question.answers.each do |answer| new_answer = answer.clone new_answer.save new_question.answers << answer end new_template.questions << question end new_template.save end 

但这确实有些奇怪的东西,实际上替换原来的答案然后创建新的答案,所以ID正确地停止匹配。

使用deep_clonable gem

 new_survey = original_survey.clone :include => [:questions => :answers] 

您可能也喜欢ActiveRecord 3.2的Amoebagem 。

在您的情况下,您可能希望使用配置DSL中可用的nullifyregexprefix选项。

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

请务必查看Amoeba文档,但使用非常简单……

只是

 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 

您还可以控制以多种方式复制哪些字段,但是,例如,如果您想要防止注释被复制但是您想要维护相同的标记,则可以执行以下操作:

 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 include_field :tags prepend :title => "Copy of " append :contents => " (copied version)" regex :contents => {:replace => /dog/, :with => "cat"} end end 

关联的递归复制很容易,也可以在子模型上启用变形虫

 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 

配置DSL有更多选项,因此请务必查看文档。

请享用! 🙂

不使用gem,您可以执行以下操作:

 class Survey < ApplicationRecord has_and_belongs_to_many :questions def copy_from(last_survey) last_survery.questions.each do |question| new_question = question.dup new_question.save questions << new_question end save end … end 

然后你可以打电话:

 new_survey = Survey.create new_survey.copy_from(past_survey) 

这将把上次调查中的所有问题复制到新的调查中并将它们联系起来。

不应该..

  new_question.answers << new_answer end new_template.questions << new_question 

您还可以为rails dup方法添加别名,如下所示:

 class Survey has_many :questions, :inverse_of=>:survey, :autosave=>true alias orig_dup dup def dup copy=orig_dup copy.questions=questions copy end end class Questions belongs_to :survey, :inverse_of=>:questions has_many :answers, :inverse_of=>:question, :autosave=>true alias orig_dup dup def dup copy=orig_dup copy.answers=answers copy end end class Answer belongs_to :question end 

然后你就可以做到这一点

 aaa = Survey.find(123).dup aaa.save