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

Ruby on-RailsActiverecordActivemodel

Ruby on-Rails Problem Overview


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

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

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.

Solution 2 - Ruby on-Rails

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

Solution 3 - Ruby on-Rails

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

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

Solution 4 - Ruby on-Rails

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

Solution 5 - Ruby on-Rails

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!

Solution 6 - Ruby on-Rails

Rails 5.1+ way:

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

Solution 7 - Ruby on-Rails

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.

Solution 8 - Ruby on-Rails

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAaron QianView Question on Stackoverflow
Solution 1 - Ruby on-RailsZubinView Answer on Stackoverflow
Solution 2 - Ruby on-RailsJeff PaquetteView Answer on Stackoverflow
Solution 3 - Ruby on-RailscolllinView Answer on Stackoverflow
Solution 4 - Ruby on-RailssharponeView Answer on Stackoverflow
Solution 5 - Ruby on-RailsTom RossiView Answer on Stackoverflow
Solution 6 - Ruby on-RailsJackaView Answer on Stackoverflow
Solution 7 - Ruby on-RailsTobiasView Answer on Stackoverflow
Solution 8 - Ruby on-RailscryptogopherView Answer on Stackoverflow