ActiveRecord: как я могу клонировать вложенные ассоциации? - PullRequest
24 голосов
/ 15 июля 2011

В настоящее время я клонирую одноуровневую ассоциацию, например:

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_many Answers.Так что 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 перестает соответствовать правильно.

Ответы [ 5 ]

45 голосов
/ 15 июля 2011

Использование deep_clonable gem

new_survey = original_survey.clone :include => [:questions => :answers]
3 голосов
/ 28 февраля 2012

Вам также может понравиться Amoeba gem для ActiveRecord 3.2.

В вашем случае вы, вероятно, захотите использовать опции nullify, regex или prefixдоступны в конфигурации DSL.

Он поддерживает простое и автоматическое рекурсивное дублирование ассоциаций has_one, has_many и has_and_belongs_to_many, предварительную обработку поля и очень гибкий и мощный конфигурационный DSL, который может применяться как кмодель и на лету.

обязательно ознакомьтесь с Документация Amoeba , но использование довольно просто ...

просто

gem install amoeba

или добавьте

gem 'amoeba'

в свой Gemfile

, затем добавьте блок amoeba в вашу модель и запустите метод 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 голос
/ 25 июля 2018

Без использования драгоценных камней, вы можете сделать следующее:

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)

Это дублирует все вопросыот последнего опроса до нового опроса и свяжите их.

0 голосов
/ 05 марта 2014

Разве это не должно быть ..

  new_question.answers << new_answer
end

new_template.questions << new_question
0 голосов
/ 23 марта 2012

Вы также можете использовать псевдоним 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...