Duplicating a record in Rails 3
Asked Answered
H

3

10

I have a prescription model in my Rails 3 application. I am trying to work out the best method of allowing records to be duplicated, but allowing the user to "review" the duplicate before it's saved.

I have read a number of questions/answers on SO (such as this one) which explain how to duplicate/clone the record and then save it - but none which explain how to show the form before save.

Reading the Rails API is appears the clone method is available.

Reading other questions and answers shows that is can be done but there is no example code apart from:

new_record = old_record.dup

The controller code I am currently working with is as follows (the model doesn't have any relationships):

  # POST /prescriptions
  # POST /prescriptions.json
  def create
    @prescription = Prescription.new(params[:prescription])
    @prescription.localip = request.env['REMOTE_ADDR']
    @prescription.employee = @prescription.employee.upcase

    respond_to do |format|
      if @prescription.save
        format.html { redirect_to @prescription, notice: 'Prescription was successfully created.' }
        format.json { render json: @prescription, status: :created, location: @prescription }
      else
        format.html { render action: "new" }
        format.json { render json: @prescription.errors, status: :unprocessable_entity }
      end
    end
  end

I am going to be linking to this clone action from the view with:

<%= link_to "Create another like this?", clone_prescription_url(@prescription), :method => :put %>

Is it as simple as adding an action to my controller like this?

def clone
 @prescription = Prescription.find(params[:id])
 @prescription.dup
 @prescription.save
end

Apologies if the above code is completely wrong, I'm trying to get my head around it! I've seen someone do exactly what I'm trying to achieve with the cloning - but not with the editing before save.

The user that's duplicating won't have permission to edit a record once saved. It's purely for the intial data entry.

Hussey answered 16/8, 2012 at 18:8 Comment(0)
C
7

If you want the clone action to allow the user to review the duplicate before it is saved (AKA created), then it is almost like the "new" action, except with filled in fields already.

So your clone method could be a modification of your new method:

def new
  @prescription = Prescription.new()
end
def clone
  @prescription = Prescription.find(params[:id]) # find original object
  @prescription = Prescription.new(@prescription.attributes) # initialize duplicate (not saved)
  render :new # render same view as "new", but with @prescription attributes already filled in
end

In the view, they can then create the object.

Clementina answered 17/8, 2012 at 7:57 Comment(0)
P
8

To do this, you're going to have to create a new instance of your Prescription class. "dup" works, but you're assuming it overwrites the existing record. Only methods that end with a bang(!) tend to do that.

Your code should be:

def clone
 @prescription = Prescription.find(params[:id])
 @new_prescription = @prescription.dup
 @new_prescription.save
end

or

def clone
 @prescription = Prescription.find(params[:id]).dup
 @prescription.save
end

This isn't testing for times when the :id isn't found.

Paz answered 17/8, 2012 at 7:55 Comment(2)
I think this is the right answer, since Rails sets the id to nil when using dup on a model, treating it as new. See apidock.com/rails/ActiveRecord/Core/dupSergo
This is what dup is meant for. Makes a shallow copy of the original, without id and timestamps, preserving only parent associations.Slotnick
C
7

If you want the clone action to allow the user to review the duplicate before it is saved (AKA created), then it is almost like the "new" action, except with filled in fields already.

So your clone method could be a modification of your new method:

def new
  @prescription = Prescription.new()
end
def clone
  @prescription = Prescription.find(params[:id]) # find original object
  @prescription = Prescription.new(@prescription.attributes) # initialize duplicate (not saved)
  render :new # render same view as "new", but with @prescription attributes already filled in
end

In the view, they can then create the object.

Clementina answered 17/8, 2012 at 7:57 Comment(0)
C
0

I was looking for logic to clone an existing record. I had to modify the logic posted by ronalchn slightly because when it tried to execute the second statement of clone I got an mass assignment error because it tried to copy id, created_at, updated_at which are not included in my attr_accessible list. This is how I modified the logic to get it to work in my application using my model:

@old_event = Event.find(params[:id]) # find original object
@event = Event.new
@event.field_1 = @old_event.field_1 (statement for each field in attar_accessible)
render :new # render same view as "new", but with @prescription attributes already filled in
Colotomy answered 27/5, 2013 at 13:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.