22

我目前克隆單層關聯像這樣: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。精細。這工作得很好。

但我遇到麻煩的是,每個問題has_manyAnswers。所以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的停止正確匹配。

回答

45

使用deep_clonable gem

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

我的天哪。我愛你。 – Shpigford

+2

克隆方法已更改爲dup。看到[回購自述](https://github.com/moiristo/deep_cloneable) –

+6

現在它變成:new_survey = original_survey.deep_clone:include => [:questions =>:答案] – halbano

3

你可能還喜歡Amoeba gem ActiveRecord的3.2。

對於您的情況,您可能想要使用配置DSL中可用的nullify,regexprefix選項。

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

您還可以控制哪些字段以多種方式被複制,但舉例來說,如果你想避免意見不被複制的,但你要保持相同的標籤,你可以做這樣的事情:

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有更多的選項,所以一定要查看文檔。

享受! :)

+1

我其實已經試過這個寶石。這是非常有前途的,但還沒有準備好。我有兩個問題,我把這兩個問題發回github項目,但最終放棄了,並使用dup手動編寫我的複製函數。我希望在幾個月內再試一次,祝你好運! – Rob

-1

您也可以別名軌道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 
0

不是應該..

new_question.answers << new_answer 
end 

new_template.questions << new_question