Reputation: 2609
The following works fine in console and does what I want (the first line would do it, the others were just to simulate the changes. Actually it was real, those were the only changes needed):
irb(main):012:0> year = Year.find(678).dup
Year Load (0.4ms) SELECT "years".* FROM "years" WHERE "years"."id" = $1 ORDER BY "years"."year_date" ASC LIMIT $2 [["id", 678], ["LIMIT", 1]]
=> #<Year id: nil, year_date: "1905-09-01", created_at: nil, updated_at: nil, resto: false, resid: true, file_basename: nil, person_id: 86, location_id: 95, title: "Resident", notes: "", resto_name: "", aws_url: nil, from_where_url: nil, caption: nil, croatian: true, from_where: nil, doc_id: 66>
irb(main):013:0> year.location_id = 211
=> 211
irb(main):014:0> year.resto = true
=> true
irb(main):015:0> year.resid = false
=> false
irb(main):016:0> year.title = "Co-proprietor"
=> "Co-proprietor"
irb(main):017:0> year.save
(0.3ms) BEGIN
Location Load (0.5ms) SELECT "locations".* FROM "locations" WHERE "locations"."id" = $1 LIMIT $2 [["id", 211], ["LIMIT", 1]]
Person Load (0.3ms) SELECT "people".* FROM "people" WHERE "people"."id" = $1 LIMIT $2 [["id", 86], ["LIMIT", 1]]
Doc Load (0.2ms) SELECT "docs".* FROM "docs" WHERE "docs"."id" = $1 LIMIT $2 [["id", 66], ["LIMIT", 1]]
Year Create (5.3ms) INSERT INTO "years" ("year_date", "created_at", "updated_at", "resto", "resid", "person_id", "location_id", "title", "notes", "resto_name", "croatian", "doc_id") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12) RETURNING "id" [["year_date", "1905-09-01"], ["created_at", "2020-05-10 15:12:37.102224"], ["updated_at", "2020-05-10 15:12:37.102224"], ["resto", true], ["resid", false], ["person_id", 86], ["location_id", 211], ["title", "Co-proprietor"], ["notes", ""], ["resto_name", ""], ["croatian", true], ["doc_id", 66]]
(40.9ms) COMMIT
=> true
I, of course, would do the changes in an edit
or new
page. I just want that page created with a duplicate of the information from the show
page I'm on. Use case is creating a bunch of record and many are almost duplicates, but does need to be done manually because there is no pattern to the information that changes.
I've created various buttons, but nothing works
<%= link_to 'Duplicate this connection (FIXME)', new_year_path(@year.dup), action: dup %>
with:
def dup
@year = Year.find(params[:id]).dup
end
Another iteration: <%= link_to 'Duplicate this connection (FIXME)', edit_year_path(@year.dup) %>
No route matches {:action=>"edit", :controller=>"years", :id=>nil}, missing required keys: [:id]
I'm lost on this, but probably not that hard.
Upvotes: 0
Views: 547
Reputation: 15985
Search the record for which you want to duplicate, call attributes
to get all attributes key-value, discard timestamp columns and pass it to create
method to create record.
def dup
year = Year.find(params[:id])
@year = Year.create!(year.attributes.except("created_at", "updated_at"))
end
Upvotes: 1
Reputation: 101811
resources :years do
get :duplicate
end
class Year < ApplicationRecord
# creates a new instance of year with a a subset of the attributes
# @return [Year]
def duplicate
# attributes returns a hash with string keys
whitelist = %w( foo bar baz )
self.class.new(attributes.slice(*whitelist))
end
end
class YearsController < ApplicationController
# ...
# GET /years/:id/duplicate
def duplicate
@year = Year.find(:id).duplicate
render :new
end
# ...
end
Its probably a good idea to whitelist the attributes you want to copy instead of blacklisting.
Upvotes: 1
Reputation: 83
When you call #.dup
on an ActiveRecord model object, which you're doing here, it copies over all attributes except for id
and the base timestamps. What this means is that you have an unpersisted object. This is why you are getting the exception messages you are getting.
Assuming you want to duplicate record 678, let's say, I'd expect a path like this:
/years/new?base_id=678
In the above, base_id=678
is a query string parameter.
You'd generate it like this:
<%= link_to "Duplicate", new_year_path(base_id: @year&.id) %>
(assuming @year
is initialized, of course)
Then, in your controller action:
def new
@year = Year.find_by(id: params[:base_id])&.dup || Year.new
end
Assuming we find the Year
record in question, we duplicate it. Otherwise, we fall back to a new Year
object and life is fine.
This should resolve your issue.
Upvotes: 1