问题
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
回答1:
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.
回答2:
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.
回答3:
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
回答4:
@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.
回答5:
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
回答6:
def change
add_timestamps :table_name
end
回答7:
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
回答8:
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.
回答9:
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
回答10:
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
回答11:
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.
回答12:
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.
回答13:
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
回答14:
It's change
, not change_table
for Rails 4.2:
class AddTimestampsToUsers < ActiveRecord::Migration
def change
add_timestamps(:users)
end
end
回答15:
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
回答16:
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
来源:https://stackoverflow.com/questions/7542976/add-timestamps-to-an-existing-table