11

I have a nested form and once I save, I want to be able to click a link on the show page to copy or clone that form and open a new one. From there I should be able to make edits (like a new id) and save as a new record. I have seen some examples like this deep_cloneable gem, but I have no idea how to implement it. I think this should be simple, but I just don't understand where to put things in the controller and in the show view.

Christopher Oezbek
  • 23,994
  • 6
  • 61
  • 85
FattRyan
  • 906
  • 2
  • 12
  • 26

4 Answers4

22

If you want to copy an activeRecord object you can use its attributes to create new one like

you can have an action in your controller which can be called on link,

def  create_from_existing
 @existing_post = Post.find(params[:id])
 #create new object with attributes of existing record 
 @post = Post.new(@existing_post.attributes) 
 render "your_post_form"
end
Isaac
  • 15,783
  • 9
  • 53
  • 76
Naren Sisodiya
  • 7,158
  • 2
  • 24
  • 35
  • Thanks, so after that goes in my controller, how should the link_to tag look in the view? – FattRyan Apr 19 '11 at 04:22
  • are you new to rails? on show page you need to render some link say link_to "Copy to new record",{:controller=>"your controller",:action=>'create_from_existing',:id=>params[:id]} also, define route inroute.rb file for create_from_existing action. if you want to show this form on existing page then you can use ajax using link_to_remote (link_to :remote=>true, rails 3) – Naren Sisodiya Apr 19 '11 at 04:29
  • 2
    How does this handle has_many? Does it create new records for the child objects or does it use the same records? – Mike Feb 03 '13 at 16:06
  • 5
    This is going to fail if `@existing_post` has any attributes that aren't mass-assignable. You must reject protected or unique attributes like `id`. – JellicleCat Sep 30 '13 at 22:55
  • 1
    There is an [answer where cloning involves keeping associated records](http://stackoverflow.com/questions/5976684/cloning-a-record-in-rails-is-it-possible-to-clone-associations-and-deep-copy) – tirdadc Jul 30 '15 at 21:33
21

I found these answers a little hard to follow. One answer shows this:

@post = Post.new(@existing_post.attributes)

which will not work as it will also pass the id, and timestamp values. I used .dup to fix that and I show that in my answer.

Here's how I achieved creating a new item from an existing item.

The model is for a Product, the controller Products_Controller.rb. We're going to add a new action to the controller called copy and we're going to link to it from the show view on an existing Product and render a filled out new view ready to be edited and saved.

First we create a route for the copy action in routes.rb

# Routes.rb
resources :Products do
  member do
    get 'copy'
  end
end

Then a copy action in Products_controller.rb

 # ProductController.rb
 def copy
   @source = Product.find(params[:id])
   @product = @source.dup
   render 'new'
 end

Now we need to add a Link to the show view to call our copy action.

# show.html.erb
<%= link_to "copy", copy_product_path(params[:id]) %>

Rails 4-6 Update:

The strong parameter scaffold makes it even shorter:

# ProductController.rb
# GET /products/1/copy
def copy
  @product = @product.dup
  render :new
end

And in the erb template:

# show.html.erb
<%= link_to "copy", copy_product_path(@product) %>
Christopher Oezbek
  • 23,994
  • 6
  • 61
  • 85
Patrick
  • 2,044
  • 1
  • 25
  • 44
3
class Foo < ActiveRecord::Base
  def self.clone_from(parent)
    parent = find(parent) unless parent.kind_of? Foo
    foo = self.new
    foo.attributes = parent.attributes
    # if you want to also clone a habtm:
    foo.some_association_ids = parent.some_association_ids
    # etc.
    foo
  end
end

class FoosController < ApplicationController
  def clone
    foo = Foo.clone_from(params[:id])
    respond_with(foo)
  end
end
scragz
  • 6,670
  • 2
  • 23
  • 23
2

Also worth mentioning is the dup method on a model. It makes a copy with all attributes and outgoing relations but sets id to nil. Like this (borrowing code from Naren Sisodiya):

def create_from_existing
  @existing_post = Post.find(params[:id])
  #create new object with attributes of existing record 
  @post = @existing_post.dup
  render "your_post_form"
end
froderik
  • 4,642
  • 3
  • 33
  • 43