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_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 ответов
Использовать deep_clonable gem
new_survey = original_survey.clone :include => [:questions => :answers]
Без использования драгоценных камней вы можете сделать следующее:
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)
Это дублирует все вопросы от предыдущего опроса до нового опроса и связывает их.
Вам также может понравиться драгоценный камень Amoeba для ActiveRecord 3.2.
В вашем случае, вы, вероятно, хотите использовать nullify
, regex
или же prefix
варианты доступны в конфигурации DSL.
Он поддерживает простое и автоматическое рекурсивное дублирование has_one
, has_many
а также has_and_belongs_to_many
ассоциации, предварительная обработка в полевых условиях и очень гибкая и мощная конфигурация 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 есть еще больше опций, поэтому обязательно ознакомьтесь с документацией.
Наслаждайтесь!:)
Не должно ли это быть..
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