web-dev-qa-db-ja.com

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は正しく一致しなくなります。

25
Shpigford

使用 deep_clonable gem

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

ActiveRecord 3.2の Amoeba gem もお勧めです。

あなたの場合、おそらく構成DSLで利用可能なnullifyregex、またはprefixオプションを利用したいと思うでしょう。

has_onehas_many、およびhas_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にはさらに多くのオプションがあるため、必ずドキュメントを確認してください。

楽しい! :)

3
Vaughn Draughon

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)

これにより、前回の調査から新しい調査までのすべての質問が複製され、それらが関連付けられます。

2
lucasarruda

そうではないはずです。

  new_question.answers << new_answer
end

new_template.questions << new_question
0
Saurabh Goyal