2

I am pretty new to Ruby on Rails and I'm having some problems. I have already checked out: This stackoverflow question about cloning a model

I am creating a new method for a model that looks like this:

def copy(new_period)
@copy = self.clone
@copy.report_id = Report.maximum(:report_id).next
@copy.period_id = new_period
@copy.save
end

I am trying to create a new instance of report that can be moved to the next year(period). When I run this method in irb I get:

irb(main):003:0> c = r.copy(5)

(1.8ms) SELECT MAX("reports"."report_id") AS max_id FROM "reports"

(0.8ms) BEGIN

(1.0ms) UPDATE "reports" SET "period_id" = 5 WHERE "reports"."report_id" = 438

(1.1ms) COMMIT

=> true

When I look in pgAdmin the new report isn't there. Could someone please explain to me what is going on when the console says "commit" and "=> true"? Does this not mean it has saved to the database?

Community
  • 1
  • 1

2 Answers2

2

You're updating the old model because the id of the model is still set. You can see that because your console shows an UPDATE instead of an INSERT statement.

From the Docs for ActiveRecord::Core#clone:

Identical to Ruby's clone method. This is a “shallow” copy. Be warned that your attributes are not copied. That means that modifying attributes of the clone will modify the original, since they will both point to the same attributes hash. If you need a copy of your attributes hash, please use the dup method.

ActiveRecord::Core#dup however results in a new record:

Duped objects have no id assigned and are treated as new records. Note that this is a “shallow” copy as it copies the object's attributes only, not its associations. The extent of a “deep” copy is application specific and is therefore left to the application to implement according to its need. The dup method does not preserve the timestamps (created|updated)_(at|on).


About your side questions:

  • BEGIN and COMMIT are used to start and end transactions in SQL.
  • true is the result of r.copy, which implicitly returns the value of @copy.safe, which returns true if the model was saved successfully.
amiuhle
  • 2,673
  • 1
  • 19
  • 28
  • Thanks for all the info! This was very helpful. – Brian Sekelsky Aug 04 '15 at 14:49
  • You're welcome. And it's perfectly normal and ok to accept and upvote an answer if it's helpful: http://meta.stackexchange.com/questions/686/accepting-answer-without-upvoting – amiuhle Aug 04 '15 at 15:33
  • Yes, I would like to upvote both the responses I got but it says I need a reputation of 15 in order to do so. I'll come back and upvote once I have that. – Brian Sekelsky Aug 04 '15 at 15:44
1

Make the method as this and try:

def copy(new_period)
  @copy = self.dup
  @copy.report_id = Report.maximum(:report_id).next
  @copy.period_id = new_period
  @copy.save
end

Because to get a copy, use the clone (or dup for rails 3.1) method:

# rails < 3.1
new_record = old_record.clone

#rails >= 3.1
new_record = old_record.dup

So what is happening currently you are cloning and it is not making a new record it is just updating the previous one each time. If you read your queries carefully:

(1.8ms) SELECT MAX("reports"."report_id") AS max_id FROM "reports"
(0.8ms) BEGIN
(1.0ms) UPDATE "reports" SET "period_id" = 5 WHERE "reports"."report_id" = 438
(1.1ms) COMMIT

It is first selecting the record and then just updating it not creating a new record (INSERT) query.

Hope this helps.

Deepesh
  • 6,138
  • 1
  • 24
  • 41