将时间戳添加到现有表

leo*_*nel 161 ruby ruby-on-rails rails-migrations rails-activerecord

我需要将时间戳(created_at updated_at)添加到现有表.我尝试了以下代码,但它没有用.我也尝试过我在网上找到的其他解决方案,但它们也不起作用.

class AddTimestampsToUser < ActiveRecord::Migration
    def change_table
        add_timestamps(:users)
    end
end
Run Code Online (Sandbox Code Playgroud)

我该怎么做?

Ben*_*son 202

时间戳帮助程序仅在create_table块中可用.您可以通过手动指定列类型来添加这些列:

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
Run Code Online (Sandbox Code Playgroud)

虽然这与add_timestamps上面指定的方法没有相同的简洁语法,但Rails仍将这些列视为时间戳列,并正常更新值.

  • `rails g migration AddTimestampsToUser created_at:datetime updated_at:datetime` - 生成上述迁移的快捷方式. (18认同)
  • 这在Rails 4中对我不起作用.以下解决方案"mu太短"正在起作用. (9认同)
  • 运行此迁移会导致错误`PG :: NotNullViolation:ERROR:列"created_at"包含空值`因为我的表已包含违反非空约束的数据.有没有比先删除非空约束然后再添加更好的方法呢? (2认同)

mu *_*ort 86

迁移只是两个类方法(或3.1中的实例方法):updown(有时change3.1中的实例方法).您希望更改进入up方法:

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
Run Code Online (Sandbox Code Playgroud)

如果您在3.1中,那么您也可以使用change(感谢Dave):

class AddTimestampsToUser < ActiveRecord::Migration
  def change
    change_table(:users) { |t| t.timestamps }
  end
end
Run Code Online (Sandbox Code Playgroud)

也许你会迷惑def change,def change_tablechange_table.

有关更多详细信息,请参阅迁移指南


geo*_*ock 72

您的原始代码非常接近正确,您只需要使用不同的方法名称.如果您使用的是Rails 3.1或更高版本,则需要定义一个change方法而不是change_table:

class AddTimestampsToUser < ActiveRecord::Migration
  def change
    add_timestamps(:users)
  end
end
Run Code Online (Sandbox Code Playgroud)

如果您使用的是旧版本,则需要定义updown方法,而不是change_table:

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

  def down
    remove_timestamps(:users)
  end
end
Run Code Online (Sandbox Code Playgroud)


Nic*_*ies 45

@ user1899434的回应是因为这里的"现有"表可能意味着一个表中已有记录,记录表明您可能不想丢弃.因此,当您使用null:false添加时间戳时,这是默认值并且通常是可取的,这些现有记录都是无效的.

但我认为通过将两个步骤组合到一个迁移中,以及使用更具语义的add_timestamps方法,可以改进答案:

def change
  add_timestamps :projects, default: Time.zone.now
  change_column_default :projects, :created_at, nil
  change_column_default :projects, :updated_at, nil
end
Run Code Online (Sandbox Code Playgroud)

您可以替换其他时间戳DateTime.now,例如,如果您希望在时间的早晨创建/更新预先存在的记录.

  • 将默认值设置为 `Time.zone.now` 有一个问题,它会返回运行迁移时创建的 Time 实例,并将该时间用作默认值。新对象不会获得新的 Time 实例。 (7认同)
  • 惊人.谢谢!只需要注意一点 - 如果我们希望我们的代码遵守正确的时区,那么应该使用"Time.zone.now". (2认同)

Pra*_*aya 38

class AddTimestampsToUser < ActiveRecord::Migration
  def change
    change_table :users do |t|
      t.timestamps
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

可用的转换是

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
Run Code Online (Sandbox Code Playgroud)

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


Ian*_*han 8

def change
  add_timestamps :table_name
end
Run Code Online (Sandbox Code Playgroud)


lig*_*yrs 7

就将时间戳列添加到具有现有数据的表而言,Nick Davies的答案是最完整的。它唯一的缺点是,它会提高ActiveRecord::IrreversibleMigrationdb:rollback

应该对它进行修改,使其可以双向使用:

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
Run Code Online (Sandbox Code Playgroud)


jle*_*sse 7

这里大多数答案的问题是,如果您默认为Time.zone.now所有记录,则会将迁移运行的时间作为默认时间,这可能不是您想要的。在 rails 5 中,您可以改为使用now(). 这会将现有记录的时间戳设置为运行迁移的时间,以及新插入记录的提交事务的开始时间。

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


shi*_*ovk 6

使用Time.current是一种很好的风格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
Run Code Online (Sandbox Code Playgroud)

或者

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
Run Code Online (Sandbox Code Playgroud)


Wes*_*ble 6

这看起来像是 Rails 5.0.7 中的一个干净的解决方案(发现了change_column_null方法):

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, :updated_at, false, Time.zone.now)
end
Run Code Online (Sandbox Code Playgroud)


小智 5

不确定这是什么时候引入的,但在 Rails 5.2.1 中你可以这样做:

class AddTimestampsToMyTable < ActiveRecord::Migration[5.2]
  def change
    add_timestamps :my_table
  end
end
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请参阅活动记录迁移文档中的“使用更改方法”。


Vis*_*ang 5

我在 Rails 5.0 上,但这些选项都不起作用。

唯一有效的是使用类型为 :timestamp 而不是 :datetime

def change
    add_column :users, :created_at, :timestamp
    add_column :users, :updated_at, :timestamp
end
Run Code Online (Sandbox Code Playgroud)