Aaron Qian
Aaron Qian

Reputation: 4505

How to determine if a record is just created or updated in after_save

The #new_record? function determines if a record has been saved. But it is always false in the after_save hook. Is there a way to determine whether the record is a newly created record or an old one from update?

I'm hoping not to use another callback such as before_create to set a flag in the model or require another query into the db.

Any advice is appreciated.

Edit: Need to determine it in after_save hook, and for my particular use case, there is no updated_at or updated_on timestamp

Upvotes: 124

Views: 78011

Answers (8)

Tobias
Tobias

Reputation: 4663

There is a method called previously_new_record? for exactly this use case.

user = User.new

user.new_record? # => true
user.previously_new_record? # => false

user.save

user.new_record? # => false
user.previously_new_record? # => true

Source: https://api.rubyonrails.org/v6.1.4/classes/ActiveRecord/Persistence.html#method-i-previously_new_record-3F

Looks like the proposed workaround by calling saved_change_to_id? doesn't work anymore. I'm on Rails 7.

Upvotes: 17

cryptogopher
cryptogopher

Reputation: 154

For Rails 4 (checked on 4.2.11.1) results of changes and previous_changes methods are empty hashes {} on object creation inside after_save. So attribute_changed? methods like id_changed? won't work as expected.

But you can take advantage of this knowledge and - knowing that at least 1 attribute has to be in changes on update - check if changes is empty. Once you confirm that it's empty, you must be during object creation:

after_save do
  if changes.empty?
    # code appropriate for object creation goes here ...
  end
end

Upvotes: 1

Jacka
Jacka

Reputation: 2590

Rails 5.1+ way:

user = User.new
user.save!
user.saved_change_to_attribute?(:id) # => true

Upvotes: 16

Zubin
Zubin

Reputation: 9742

I was looking to use this for an after_save callback.

A simpler solution is to use id_changed? (since it won't change on update) or even created_at_changed? if timestamp columns are present.

Update: As @mitsy points out, if this check is needed outside of callbacks then use id_previously_changed?. See docs.

Upvotes: 199

Tom Rossi
Tom Rossi

Reputation: 12086

Since the object has already been saved, you would you need to look at the previous changes. The ID should only change after a create.

# true if this is a new record
@object.previous_changes[:id].any?

There is also an instance variable @new_record_before_save. You can access that by doing the following:

# true if this is a new record
@object.instance_variable_get(:@new_record_before_save)

Both are pretty ugly, but they would allow you to know whether the object has been newly created. Hope that helps!

Upvotes: 19

sharpone
sharpone

Reputation: 332

There is an after_create callback which is only called if the record is a new record, after it is saved. There is also an after_update callback for use if this was an existing record which was changed and saved. The after_save callback is called in both cases, after either after_create or after_update is called.

Use after_create if you need something to happen once after a new record has been saved.

More info here: http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html

Upvotes: 23

colllin
colllin

Reputation: 9789

Yet another option, for those who do have an updated_at timestamp:

if created_at == updated_at
  # it's a newly created record
end

Upvotes: 30

Jeff Paquette
Jeff Paquette

Reputation: 7127

No rails magic here that I know of, you'll have to do it yourself. You could clean this up using a virtual attribute...

In your model class:

def before_save
  @was_a_new_record = new_record?
  return true
end

def after_save
  if @was_a_new_record
    ...
  end
end

Upvotes: 33

Related Questions