ActiveRecord: How can I clone nested associations?

前端 未结 5 1147
星月不相逢
星月不相逢 2020-12-05 18:17

I\'m currently cloning a single-level association like this:

class Survey < ActiveRecord::Base
  def duplicate
    new_template = self.clone
    new_templ         


        
相关标签:
5条回答
  • 2020-12-05 18:21

    Without using gems, you can do the following:

    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.

    0 讨论(0)
  • 2020-12-05 18:23

    You can also alias the rails dup method, as follows:

    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
    
    0 讨论(0)
  • 2020-12-05 18:28

    You may also like the Amoeba gem for ActiveRecord 3.2.

    In your case, you probably want to make use of the nullify, regex or prefix options available in the configuration DSL.

    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.

    be sure to check out the Amoeba Documentation but usage is pretty easy...

    just

    gem install amoeba
    

    or add

    gem 'amoeba'
    

    to your Gemfile

    then add the amoeba block to your model and run the dup method as usual

    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.

    Enjoy! :)

    0 讨论(0)
  • 2020-12-05 18:36

    Shouldn't it be..

      new_question.answers << new_answer
    end
    
    new_template.questions << new_question
    
    0 讨论(0)
  • 2020-12-05 18:39

    Use deep_clonable gem

    new_survey = original_survey.clone :include => [:questions => :answers]
    
    0 讨论(0)
提交回复
热议问题