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中可用的nullify
, regex
或prefix
选项。
它支持has_one
, has_many
和has_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