Add timestamps to an existing table

Ruby on-RailsRubyRails ActiverecordRails Migrations

Ruby on-Rails Problem Overview


I need to add timestamps (created_at & updated_at) to an existing table. I tried the following code but it didn't work.

class AddTimestampsToUser < ActiveRecord::Migration
    def change_table
        add_timestamps(:users)
    end
end

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

The timestamp helper is only available in the create_table block. You can add these columns by specifying the column types manually:

class AddTimestampsToUser < ActiveRecord::Migration
  def change_table
    add_column :users, :created_at, :datetime, null: false
    add_column :users, :updated_at, :datetime, null: false
  end
end

While this does not have the same terse syntax as the add_timestamps method you have specified above, Rails will still treat these columns as timestamp columns, and update the values normally.

Solution 2 - Ruby on-Rails

Migrations are just two class methods (or instance methods in 3.1): up and down (and sometimes a change instance method in 3.1). You want your changes to go into the up method:

class AddTimestampsToUser < ActiveRecord::Migration
  def self.up # Or `def up` in 3.1
    change_table :users do |t|
      t.timestamps
    end
  end
  def self.down # Or `def down` in 3.1
    remove_column :users, :created_at
    remove_column :users, :updated_at
  end
end

If you're in 3.1 then you could also use change (thanks Dave):

class AddTimestampsToUser < ActiveRecord::Migration
  def change
    change_table(:users) { |t| t.timestamps }
  end
end

Perhaps you're confusing def change, def change_table, and change_table.

See the migration guide for further details.

Solution 3 - Ruby on-Rails

Your original code is very close to right, you just need to use a different method name. If you're using Rails 3.1 or later, you need to define a change method instead of change_table:

class AddTimestampsToUser < ActiveRecord::Migration
  def change
    add_timestamps(:users)
  end
end

If you're using an older version you need to define up and down methods instead of change_table:

class AddTimestampsToUser < ActiveRecord::Migration
  def up
    add_timestamps(:users)
  end

  def down
    remove_timestamps(:users)
  end
end

Solution 4 - Ruby on-Rails

@user1899434's response picked up on the fact that an "existing" table here could mean a table with records already in it, records that you might not want to drop. So when you add timestamps with null: false, which is the default and often desirable, those existing records are all invalid.

But I think that answer can be improved upon, by combining the two steps into one migration, as well as using the more semantic add_timestamps method:

def change
  add_timestamps :projects, default: Time.zone.now
  change_column_default :projects, :created_at, nil
  change_column_default :projects, :updated_at, nil
end

You could substitute some other timestamp for DateTime.now, like if you wanted preexisting records to be created/updated at the dawn of time instead.

Solution 5 - Ruby on-Rails

class AddTimestampsToUser < ActiveRecord::Migration
  def change
    change_table :users do |t|
      t.timestamps
    end
  end
end

Available transformations are

change_table :table do |t|
  t.column
  t.index
  t.timestamps
  t.change
  t.change_default
  t.rename
  t.references
  t.belongs_to
  t.string
  t.text
  t.integer
  t.float
  t.decimal
  t.datetime
  t.timestamp
  t.time
  t.date
  t.binary
  t.boolean
  t.remove
  t.remove_references
  t.remove_belongs_to
  t.remove_index
  t.remove_timestamps
end

http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Table.html

Solution 6 - Ruby on-Rails

Nick Davies answer is the most complete in terms of adding timestamp columns to a table with existing data. Its only downside is that it will raise ActiveRecord::IrreversibleMigration on a db:rollback.

It should be modified like so to work in both directions:

def change
  add_timestamps :campaigns, default: DateTime.now
  change_column_default :campaigns, :created_at, from: DateTime.now, to: nil
  change_column_default :campaigns, :updated_at, from: DateTime.now, to: nil
end

Solution 7 - Ruby on-Rails

def change
  add_timestamps :table_name
end

Solution 8 - Ruby on-Rails

I'm on rails 5.0 and none of these options worked.

The only thing that worked was using the type to be :timestamp and not :datetime

def change
    add_column :users, :created_at, :timestamp
    add_column :users, :updated_at, :timestamp
end

Solution 9 - Ruby on-Rails

The issue with most of the answers here is that if you default to Time.zone.now all records will have the time that the migration was run as their default time, which is probably not what you want. In rails 5 you can instead use now(). This will set the timestamps for existing records as the time the migration was run, and as the start time of the commit transaction for newly inserted records.

class AddTimestampsToUsers < ActiveRecord::Migration def change add_timestamps :users, default: -> { 'now()' }, null: false end end

Solution 10 - Ruby on-Rails

Using Time.current is a good style https://github.com/rubocop-hq/rails-style-guide#timenow

def change
  change_table :users do |t|
    t.timestamps default: Time.current
    t.change_default :created_at, from: Time.current, to: nil
    t.change_default :updated_at, from: Time.current, to: nil
  end
end

or

def change
  add_timestamps :users, default: Time.current
  change_column_default :users, :created_at, from: Time.current, to: nil
  change_column_default :users, :updated_at, from: Time.current, to: nil
end

Solution 11 - Ruby on-Rails

not sure when exactly this was introduced, but in rails 5.2.1 you can do this:

class AddTimestampsToMyTable < ActiveRecord::Migration[5.2]
  def change
    add_timestamps :my_table
  end
end

for more see "using the change method" in the active record migrations docs.

Solution 12 - Ruby on-Rails

This seems like a clean solution in Rails 5.0.7 (discovered the change_column_null method):

def change
  add_timestamps :candidate_offices, default: nil, null: true
  change_column_null(:candidate_offices, :created_at, false, Time.zone.now)
  change_column_null(:candidate_offices, :created_at, false, Time.zone.now)
end

Solution 13 - Ruby on-Rails

A lot of answers here, but I'll post mine too because none of the previous ones really worked for me :)

As some have noted, #add_timestamps unfortunately adds the null: false restriction, which will cause old rows to be invalid because they don't have these values populated. Most answers here suggest that we set some default value (Time.zone.now), but I wouldn't like to do that because these default timestamps for old data will not be correct. I don't see the value in adding incorrect data to the table.

So my migration was simply:

class AddTimestampsToUser < ActiveRecord::Migration
  def change_table
    add_column :projects, :created_at, :datetime
    add_column :projects, :updated_at, :datetime
  end
end

No null: false, no other restrictions. Old rows will continue being valid with created_at as NULL, and update_at as NULL (until some update is performed to the row). New rows will have created_at and updated_at populated as expected.

Solution 14 - Ruby on-Rails

I made a simple function that you can call to add to each table (assuming you have a existing database) the created_at and updated_at fields:

  # add created_at and updated_at to each table found.
  def add_datetime
    tables = ActiveRecord::Base.connection.tables
    tables.each do |t|
      ActiveRecord::Base.connection.add_timestamps t  
    end    
  end

Solution 15 - Ruby on-Rails

> add_timestamps(table_name, options = {}) public

Adds timestamps (created_at and updated_at) columns to table_name. Additional options (like null: false) are forwarded to #add_column.

class AddTimestampsToUsers < ActiveRecord::Migration
  def change
    add_timestamps(:users, null: false)
  end
end

Solution 16 - Ruby on-Rails

This is a simple one to add timestamp in existing table.

class AddTimeStampToCustomFieldMeatadata < ActiveRecord::Migration
  def change
    add_timestamps :custom_field_metadata
  end
end

Solution 17 - Ruby on-Rails

The answers before seem right however I faced issues if my table already has entries.

I would get 'ERROR: column created_at contains null values'.

To fix, I used:

def up
  add_column :projects, :created_at, :datetime, default: nil, null: false
  add_column :projects, :updated_at, :datetime, default: nil, null: false
end

I then used the gem migration_data to add the time for current projects on the migration such as:

def data
  Project.update_all created_at: Time.now
end

Then all projects created after this migration will be correctly updated. Make sure the server is restarted too so that Rails ActiveRecord starts tracking the timestamps on the record.

Solution 18 - Ruby on-Rails

For those who don't use Rails but do use activerecord, the following also adds a column to an existing model, example is for an integer field.

ActiveRecord::Schema.define do
  change_table 'MYTABLE' do |table|
    add_column(:mytable, :my_field_name, :integer)
  end
end

Solution 19 - Ruby on-Rails

It's change, not change_table for Rails 4.2:

class AddTimestampsToUsers < ActiveRecord::Migration
  def change
    add_timestamps(:users)
  end
end

Solution 20 - Ruby on-Rails

I personally used the following, and it updated all previous records with the current time/date:

add_column :<table>, :created_at, :datetime, default: Time.zone.now, null: false
add_column :<table>, :updated_at, :datetime, default: Time.zone.now, null: false

Solution 21 - Ruby on-Rails

I ran into the same issue on Rails 5 trying to use

change_table :my_table do |t|
    t.timestamps
end

I was able to add the timestamp columns manually with the following:

change_table :my_table do |t|
    t.datetime :created_at, null: false, default: DateTime.now
    t.datetime :updated_at, null: false, default: DateTime.now
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
QuestionleonelView Question on Stackoverflow
Solution 1 - Ruby on-RailsBen SimpsonView Answer on Stackoverflow
Solution 2 - Ruby on-Railsmu is too shortView Answer on Stackoverflow
Solution 3 - Ruby on-RailsgeorgebrockView Answer on Stackoverflow
Solution 4 - Ruby on-RailsNick DaviesView Answer on Stackoverflow
Solution 5 - Ruby on-RailsPradeep SanjayaView Answer on Stackoverflow
Solution 6 - Ruby on-RailslightyrsView Answer on Stackoverflow
Solution 7 - Ruby on-RailsIan VaughanView Answer on Stackoverflow
Solution 8 - Ruby on-RailsVishnu NarangView Answer on Stackoverflow
Solution 9 - Ruby on-RailsjlesseView Answer on Stackoverflow
Solution 10 - Ruby on-RailsshilovkView Answer on Stackoverflow
Solution 11 - Ruby on-RailsReef LorettoView Answer on Stackoverflow
Solution 12 - Ruby on-RailsWes GambleView Answer on Stackoverflow
Solution 13 - Ruby on-RailsKostisView Answer on Stackoverflow
Solution 14 - Ruby on-RailsRogerView Answer on Stackoverflow
Solution 15 - Ruby on-RailsalmawhoobView Answer on Stackoverflow
Solution 16 - Ruby on-RailsDinesh VaitageView Answer on Stackoverflow
Solution 17 - Ruby on-RailsdbrodyView Answer on Stackoverflow
Solution 18 - Ruby on-RailspeterView Answer on Stackoverflow
Solution 19 - Ruby on-RailsIgorView Answer on Stackoverflow
Solution 20 - Ruby on-RailsJaimeView Answer on Stackoverflow
Solution 21 - Ruby on-RailsAndres RosalesView Answer on Stackoverflow