简体   繁体   English

ActiveRecord:如何克隆嵌套关联?

[英]ActiveRecord: How can I clone nested associations?

I'm currently cloning a single-level association like this:我目前正在克隆这样的单级关联:

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

So that clones the Survey then clones the Questions associated with that survey.因此,克隆Survey然后克隆与该调查相关的Questions Fine.美好的。 That works quite well.这工作得很好。

But what I'm having trouble with is that each question has_many Answers .但我遇到的问题是每个问题has_many Answers So Survey has_many Questions which has_many Answers .所以Survey has_many Questions which has_many Answers

I can't figure out how to clone the answers properly.我不知道如何正确克隆答案。 I've tried this:我试过这个:

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

But that does some weird stuff with actually replacing the original answers then creating new ones, so ID's stop matching correctly.但这确实会发生一些奇怪的事情,即实际替换原始答案然后创建新答案,因此 ID 停止正确匹配。

Use deep_clonable gem使用deep_clonable gem

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

You may also like the Amoeba gem for ActiveRecord 3.2.您可能还喜欢 ActiveRecord 3.2 的Amoeba gem

In your case, you probably want to make use of the nullify , regex or prefix options available in the configuration DSL.在您的情况下,您可能希望使用配置 DSL 中可用的nullifyregexprefix选项。

It supports easy and automatic recursive duplication of has_one , has_many and has_and_belongs_to_many associations, field preprocessing and a highly flexible and powerful configuration DSL that can be applied both to the model and on the fly.它支持简单且自动的has_onehas_manyhas_and_belongs_to_many关联的递归复制、字段预处理和高度灵活且功能强大的配置 DSL,可同时应用于 model 和运行中。

be sure to check out the Amoeba Documentation but usage is pretty easy...请务必查看Amoeba 文档,但使用起来非常简单......

just只是

gem install amoeba

or add或添加

gem 'amoeba'

to your Gemfile到您的 Gemfile

then add the amoeba block to your model and run the dup method as usual然后将变形虫块添加到您的 model 并像往常一样运行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

You can also control which fields get copied in numerous ways, but for example, if you wanted to prevent comments from being duplicated but you wanted to maintain the same tags, you could do something like this:您还可以通过多种方式控制复制哪些字段,但是例如,如果您想防止注释被复制但又想保持相同的标签,则可以执行以下操作:

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

  amoeba do
    exclude_field :comments
  end
end

You can also preprocess fields to help indicate uniqueness with both prefixes and suffixes as well as regexes.您还可以预处理字段以帮助指示前缀和后缀以及正则表达式的唯一性。 In addition, there are also numerous options so you can write in the most readable style for your purpose:此外,还有许多选项,因此您可以根据自己的目的以最易读的风格编写:

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

Recursive copying of associations is easy, just enable amoeba on child models as well关联的递归复制很容易,只需在子模型上启用变形虫

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

The configuration DSL has yet more options, so be sure to check out the documentation.配置 DSL 有更多选项,因此请务必查看文档。

Enjoy: :)享受: :)

Without using gems, you can do the following:在不使用 gems 的情况下,您可以执行以下操作:

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

Then you can call:然后你可以调用:

new_survey = Survey.create
new_survey.copy_from(past_survey)

That will duplicate all questions from last Survey to new Survey and tie them.这会将上次调查中的所有问题复制到新调查中并将它们联系起来。

Shouldn't it be..不应该吗。。

  new_question.answers << new_answer
end

new_template.questions << new_question

You can also alias the rails dup method, as follows:您还可以为 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

and then you can do this然后你可以这样做

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM