Is there a way to avoid automatically updating Rails timestamp fields?
Ruby on-RailsRubyActiverecordTimestampRuby on-Rails Problem Overview
If you have DB columns created_at
and updated_at
Rails will automatically set those values when you create and update a model object. Is there a way to save the model without touching those columns?
I am bringing in some legacy data and I would like to set those values from the corresponding values in the (differently named) legacy data fields. I'm finding when I set them on the model and then save the model, Rails appears to override the incoming values.
Of course I could just name the Rails model columns differently to prevent that, but after the data is imported, I want Rails to do its automatic timestamp thing.
Ruby on-Rails Solutions
Solution 1 - Ruby on-Rails
Do this in a migration or in a rake task (or in the new database seeds if you're on edge rails):
ActiveRecord::Base.record_timestamps = false
begin
run_the_code_that_imports_the_data
ensure
ActiveRecord::Base.record_timestamps = true # don't forget to enable it again!
end
You can safely set created_at
and updated_at
manually, Rails won't complain.
Note:
This also works on individual models, e.g.
User.record_timestamps = false
Solution 2 - Ruby on-Rails
use update_column method instead:
http://api.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-update_column
update_column(name, value)
# Updates a single attribute of an object, without calling save.
Validation is skipped.
Callbacks are skipped.
updated_at/updated_on column is not updated if that column is available.
Raises an ActiveRecordError when called on new objects, or when the name attribute is marked as readonly.
Solution 3 - Ruby on-Rails
Rails 5 provides a convenient way to update a record without updating it's timestamp updated_at
: https://api.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-save
You, just need to pass touch:false
while updating your record.
>> user = User.first
>> user.updated_at
=> Thu, 28 Apr 2016 20:01:57 IST +05:30
>> user.name = "Jose"
>> user.save(touch: false)
=> true
>> user.updated_at
=> Thu, 28 Apr 2016 20:01:57 IST +05:30
Solution 4 - Ruby on-Rails
You can set the following inside your migration:
ActiveRecord::Base.record_timestamps = false
Or altenatively use update_all:
> update_all(updates, conditions = nil, options = {}) > > Updates all records with details given > if they match a set of conditions > supplied, limits and order can also be > supplied. This method constructs a > single SQL UPDATE statement and sends > it straight to the database. It does > not instantiate the involved models > and it does not trigger Active Record > callbacks.
Solution 5 - Ruby on-Rails
In Rails 3+, for a single object, set record_timestamps
for the object rather than class. I.e.,
>> user = User.first
>> user.updated_at
=> Tue, 12 Apr 2016 22:47:51 GMT +00:00
>> user.name = "Jose"
=> "Jose"
>> user.record_timestamps = false
>> user.save
=> true
>> user.updated_at
=> Tue, 12 Apr 2016 22:47:51 GMT +00:00
>> User.record_timestamps
=> true
This way, you don't touch global state for the model, and you don't have to remember to restore the prior setting in an ensure
block.
Solution 6 - Ruby on-Rails
Since this is a one-time import, you could do the following:
- Create model using
legacy_created_at
andlegacy_updated_at
fields. - Load legacy data. Map into the model fields as desired. You can use
#save
and generally not worry about usingupdate_all
or the like, and you can use callbacks if desired. - Create a migration to rename the columns to
created_at
andupdated_at
.
Solution 7 - Ruby on-Rails
I like to use a mixin module to temporarily turn off time-stamping in a block:
module WithoutTimestamps
def without_timestamps
old = ActiveRecord::Base.record_timestamps
ActiveRecord::Base.record_timestamps = false
begin
yield
ensure
ActiveRecord::Base.record_timestamps = old
end
end
end
Then you can use it wherever you need it
class MyModel < ActiveRecord::Base
include WithoutTimestamps
def save_without_timestamps
without_timestamps do
save!
end
end
end
Or just a one-off like this:
m = MyModel.find(1)
WithoutTimestamps.without_timestamps do
m.save!
end
Solution 8 - Ruby on-Rails
Referring to other answers, I found to my surprise that disabling timestamps for a single model, like in:
User.record_timestamps = false
worked for my development database, but not on my pre-production database, which runs on a different server. However it works if I disable timestamps for all models with
ActiveRecord::Base.record_timestamps = false
(Situation: modifying the created_at attribute in a migration)
Solution 9 - Ruby on-Rails
When not in a bulk import, you can override the should_record_timestamps? method on your model to add new checks on when to update the updated_at column.
Solution 10 - Ruby on-Rails
Or, for thread safety, see: http://www.hungryfools.com/2007/07/turning-off-activerecord-timestamp.html
Solution 11 - Ruby on-Rails
I wasn't able to make ActiveRecord::Base.record_timestamps
flag change anything, and the only working way is using ActiveRecord::Base.no_touching {}
:
ActiveRecord::Base.no_touching do
Project.first.touch # does nothing
Message.first.touch # does nothing
end
Project.no_touching do
Project.first.touch # does nothing
Message.first.touch # works, but does not touch the associated project
end
Taken from here.
This is very useful for rake tasks where you have to override some properties of deeply-related models and don't want to bother about inner callbacks, while your users rely on created_at
or updated_at
attributes or those are used as sorting columns.
Solution 12 - Ruby on-Rails
I'm late to the party but this is how we skip updating updated_at
in my shop:
# config/initializers/no_timestamping.rb
module ActiveRecord
class Base
def update_record_without_timestamping
class << self
def record_timestamps; false; end
end
save!
class << self
remove_method :record_timestamps
end
end
end
end
Usage:
post.something = 'whatever'
post.update_record_without_timestamping