将时间戳添加到现有表


Answers:


210

时间戳帮助器仅在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

尽管它的语法与add_timestamps上面指定的方法不同,但Rails仍会将这些列视为timestamp列,并正常更新值。


10
在Rails 4中,这对我不起作用。下面的“ mu is too short”解决方案正在起作用。
newUserNameHere 2014年

21
rails g migration AddTimestampsToUser created_at:datetime updated_at:datetime-生成上述迁移的快捷方式。
Konstantine Kalbazov 2014年

2
运行此迁移会导致错误, PG::NotNullViolation: ERROR: column "created_at" contains null value 因为我的表已包含违反非null约束的数据。有什么比首先删除not null约束然后再添加它更好的方法呢?
哈比卜

1
@ M.Habib我不这么认为,但是这个答案很好地将其封装在一个迁移中。
littleforest '18

1
@ M.Habib取决于您认为对默认值最有意义的方式,您可以这样做add_column :users, :updated_at, :datetime, null: false, default: Time.zone.nowTime.zone.now只是一个例子,您应该使用对逻辑有意义的任何值。
Delong Gao

91

迁移只是两个类方法(或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

如果您使用的是3.1,则还可以使用change(感谢Dave):

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

也许你会迷惑def changedef change_tablechange_table

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


1
(好吧,change现在有方法了,尽管在这种情况下不是问题:)
Dave Newton

@Dave:的确如此,我为了避免版本问题去了泛型,但是change值得一提,所以我也将其添加。
亩太短

真正的亩,但我听说3.1确实改变了这种情况,“下跌”的趋势已经消失。Rails会自动找出down方法。你听说过吗?
Michael Durrant

@Michael:我一直在与正在使用的3.1应用程序一起专门使用MongoDB,所以我还没有使用3.1 AR迁移。该文档表明一切都在朝实例方法发展(出于未知原因)。
亩太短

@MichaelDurrant,在很多情况下现在都无法解决“更改”问题,如果向上/向下消失,将会有些生气:)(在更改迁移中添加“除非”子句,以避免迁移冲突,然后尝试回滚...)即使在您发表此评论3年后,我也不认为它会改变。:)
frandroid

76

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

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

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

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

  def down
    remove_timestamps(:users)
  end
end

58

@ 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

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


2
惊人。谢谢!请注意,Time.zone.now如果我们希望我们的代码遵循正确的时区,则应使用该注释。
约翰·加拉格尔

4
将默认值设置为默认值存在一个问题,Time.zone.now即它将返回运行迁移时创建的Time实例,并且仅将该时间用作默认值。新对象将不会获得新的Time实例。
Tovi Newman

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

可用的转换是

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


10

就将时间戳列添加到具有现有数据的表而言,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

如on Rails的4.2.7(我觉得对我这样写没工作完全change_column_default不支持from,并to在该版本?),但我把这个想法和创建up/down方法,而不是一个单一的change方法和它的工作就像一个魅力!
加尔


4

不知道是什么时候引入的,但是在rails 5.2.1中可以执行以下操作:

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

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


我没有使其与Migration [5.1]一起使用;然后我将数字更改为[5.2],Rails告诉我只能使用5.1、5.0或4.2。我尝试使用5.0失败,然后尝试使用4.2成功。
是Ma

老了,我知道,但如果你有现有记录添加:, null: true:my_table
JOMAR

2

我做了一个简单的函数,可以调用它添加到每个表(假设您有一个现有数据库)中的created_atUpdated_at字段:

  # 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

2

add_timestamps(table_name,options = {})公共

将时间戳(created_at和updated_at)列添加到table_name。其他选项(如null:false)将转发到#add_column。

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

1

之前的答案似乎是正确的,但是如果我的表已经有条目,我就会遇到问题。

我会收到“错误:列created_at包含null值”。

要修复,我使用了:

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

然后,我使用gem migration_data添加当前项目的迁移时间,例如:

def data
  Project.update_all created_at: Time.now
end

然后,将正确更新此迁移后创建的所有项目。确保服务器也重新启动,以便Rails ActiveRecord开始跟踪记录上的时间戳。


1

这里有很多答案,但是我也要发布我的答案,因为先前的答案对我来说都没有用:)

如某些人所述,#add_timestamps不幸的是增加了null: false限制,这将导致旧行无效,因为它们没有填充这些值。这里的大多数答案都建议我们设置一些默认值(Time.zone.now),但我不想这样做,因为这些用于旧数据的默认时间戳将不正确。我看不到在表中添加错误数据的价值。

所以我的迁移很简单:

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

null: false,没有其他限制。旧行将继续以created_atas NULLupdate_atas 有效NULL(直到对该行执行了一些更新)。新行会created_atupdated_at人口预期。


1

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

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


1

使用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

要么

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

1

这是在现有表中添加时间戳的简单方法。

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

0

对于那些不使用Rails而是使用activerecord的用户,以下代码还将列添加到现有模型中,例如整数字段的示例。

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

0

change,不适change_table用于Rails 4.2:

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

0

在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, :created_at, false, Time.zone.now)
end

0

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

唯一起作用的是使用类型为:timestamp而不是:datetime

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

-1

我个人使用以下内容,并使用当前时间/日期更新了所有以前的记录:

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

-2

我在尝试使用Rails 5时遇到了相同的问题

change_table :my_table do |t|
    t.timestamps
end

我可以使用以下命令手动添加时间戳列:

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

这不会总是在运行迁移时设置默认值吗?(因此,不是由DB处理的动态时间戳)
Guillaume Petit

对于数据库中已经存在的记录,是的,它将把created_at和updated_at设置为运行迁移的日期。但是,如果没有事先拥有这些值,请了解您如何初始化这些值。编辑:这将仅被视为该行历史的开始
安德烈·罗萨莱斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.