What is the easiest way to duplicate an activerecord record?
To get a copy, use the clone (or dup for rails 3.1+) method:
# rails < 3.1new_record = old_record.clone#rails >= 3.1new_record = old_record.dup
Then you can change whichever fields you want.
ActiveRecord overrides the built-in Object#clone to give you a new (not saved to the DB) record with an unassigned ID.
Note that it does not copy associations, so you'll have to do this manually if you need to.
Depending on your needs and programming style, you can also use a combination of the new method of the class and merge. For lack of a better simple example, suppose you have a task scheduled for a certain date and you want to duplicate it to another date. The actual attributes of the task aren't important, so:
old_task = Task.find(task_id)new_task = Task.new(old_task.attributes.merge({:scheduled_on => some_new_date}))
will create a new task with :id => nil
, :scheduled_on => some_new_date
, and all other attributes the same as the original task. Using Task.new, you will have to explicitly call save, so if you want it saved automatically, change Task.new to Task.create.
Peace.
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 endendclass Comment < ActiveRecord::Base belongs_to :postendclass Tag < ActiveRecord::Base has_and_belongs_to_many :postsendclass PostsController < ActionController def some_method my_post = Post.find(params[:id]) new_post = my_post.dup new_post.save endend
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 endend
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"} endend
Recursive copying of associations is easy, just enable amoeba on child models as well
class Post < ActiveRecord::Base has_many :comments amoeba do enable endendclass Comment < ActiveRecord::Base belongs_to :post has_many :ratings amoeba do enable endendclass Rating < ActiveRecord::Base belongs_to :commentend
The configuration DSL has yet more options, so be sure to check out the documentation.
Enjoy! :)