Is there a way to avoid automatically updating Rails timestamp fields?

Viewed 39222

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.

12 Answers

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

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.

Since this is a one-time import, you could do the following:

  1. Create model using legacy_created_at and legacy_updated_at fields.
  2. Load legacy data. Map into the model fields as desired. You can use #save and generally not worry about using update_all or the like, and you can use callbacks if desired.
  3. Create a migration to rename the columns to created_at and updated_at.

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.

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
Related