如何为datetime列设置默认值以记录迁移中的创建时间?


114

考虑下面的表创建脚本:

create_table :foo do |t|
  t.datetime :starts_at, :null => false
end

是否可以将默认值设置为当前时间?

我正在尝试为下面给出的SQL列定义在Rails中找到与DB独立的等效项:

Oracle语法

start_at DATE DEFAULT SYSDATE() 

MySQL语法

start_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP

要么

start_at DATETIME DEFAULT NOW()

Answers:


173

Rails 5现在支持此功能。

这是一个示例迁移:

class CreatePosts < ActiveRecord::Migration[5.0]
  def change
    create_table :posts do |t|
      t.datetime :modified_at, default: -> { 'CURRENT_TIMESTAMP' }
      t.timestamps
    end
  end 
end

参见https://github.com/rails/rails/issues/27077上的讨论,并通过prathamesh-sonpatki回答


14
好答案。顺便说一句,要知道在Postgres CURRENT_TIMESTAMP中将是当前事务开始的时间,因此在同一事务中创建的多个记录将获得相同的值。如果您希望语句执行的当前当前时间(忽略事务上下文),请签出CLOCK_TIMESTAMP
安倍·佛尔克

我添加了这样的内容:add_column :table_name, :start_date, :datetime, default: -> { 'CURRENT_TIMESTAMP' }这就是schema.rb中看起来的样子,t.datetime "start_date", default: -> { "now()" }但是当我创建新记录时,它并没有被填充。知道为什么吗?
Sandip Subedi '19

@SandipSubedi对我一样。在轨道上5.2.3。
courtsimas

1
您需要执行@courtsimas post.reload以获得这些值。它在这里解释:stackoverflow.com/questions/53804787/...
的Sandip苏贝迪

1
@SandipSubedi我意识到我发表评论后的5分钟。就像生成uuid一样。谢谢!
courtsimas

119

您可以在这样的模型中添加函数:

  before_create :set_foo_to_now
  def set_foo_to_now
    self.foo = Time.now
  end

这样,模型将在模型中设置当前时间。

您还可以在迁移中放置一些sql代码,以在数据库级别设置默认值,例如:

execute 'alter table foo alter column starts_at set default now()'

设置如下所示:

create_table :foo do |t|
  t.datetime :starts_at, :null => false, :default => Time.now
end

导致在迁移过程中执行Time.now函数,因此将在数据库中创建表,如下所示:

create table foo ( starts_at timestamp not null default '2009-01-01 00:00:00');

但我认为这不是您想要的。


1
目前,我正在:before_create回调中设置该值。<br>我正在这里寻找某种类型的AR魔术。我花了一些时间查看Rails代码,但是没有找到任何解决方案。我以为我会问周围是否有其他选择。
Harish Shetty

我建议通过在before_create上进行回调来实现。
琼妮

1
我不想更改数据库表,因为我想保持我的代码数据库中立。我希望AR可以使用某种机制为Datetime字段设置默认值,类似于created_at字段。
哈里什·谢蒂

9
请注意,before_create回调在插入数据库之前但实例化对象之后(如new())运行。因此self.foo = Time.now将覆盖您可能提供的值new()。我建议self.foo = Time.current unless self.foo.present?改为。
法提赫2014年

2
并非如此,当使用execute时,这将:starts_at, :default => 'now()'在schema.rb中放置一行。但是,当使用rake db:schema:dump它将覆盖schema.rb :starts_at, :default => '2015-05-29 09:46:33'(或启动脚本的任何日期)时,这将无法正常工作……可悲....
astreal 2015年

13

如果表具有名为的字段,则Active Record会自动为创建和更新操作添加时间戳记 created_at / created_onupdated_at/的updated_on来源-api.rubyonrails.org

除了拥有该列之外,您无需执行任何其他操作。


我的表中已经有这些字段。我的调度程序需要一个附加字段来保存开始日期。当前,我正在使用:before_create回调来设置当前日期。如果我经常遇到这种情况,我不得不求助于编写插件来更改ColumnDefinition类的“ to_sql”方法中的默认值处理。
Harish Shetty


9

我通常这样做:

def change
  execute("
    ALTER TABLE your_table
    ALTER COLUMN your_column
    SET DEFAULT CURRENT_TIMESTAMP
  ")
end

因此,您schema.rb将拥有类似以下内容:

create_table "your_table", force: :cascade do |t|
  t.datetime "your_column", default: "now()"
end

缺点:此解决方案仅在您运行时有效rake db:migrate,而在您使用加载架构文件时则无效rake db:schema:load
Alter Lagos

8

如果你需要改变 Rails 5中的现有DateTime列(而不是按照其他答案中的说明创建新表),以便可以利用默认日期功能,则可以创建如下迁移:

class MakeStartsAtDefaultDateForFoo < ActiveRecord::Migration[5.0]
  def change
    change_column :foos, :starts_at, :datetime, default: -> { 'CURRENT_TIMESTAMP' }
  end
end

不好的形式,是对某事投反对票,却不解释答案是什么问题。有人知道为什么这个被否决吗?如果要更改一列而不是创建一列,它将显示语法。
马特·朗

我不是投票否决的人,但是我很难看到这个答案与您的遗嘱答案之前一年的答案有何不同。问题是关于设置默认值,您的答案具有相同的lambda子句。
nurettin

2
@nurettin我明白您的意思,但是在我的辩护中,用于创建新列的语法与更改现有列的语法略有不同。为那些试图通过网络搜索找到问题的人提供一种语法,同时尝试将默认值添加到当前数据模型中,而不是完全创建一个新的模型/表,这可能会很有帮助。没有?您假设每个人都知道他们可以为change_column使用相同的lambda。也许他们应该意识到这一点,但这就是我在这里回答的原因-因此,他们不必去其他任何地方就可以解决这一问题。干杯!
马特·朗

4
FWIW我刚才使用了这种语法,并且很欣赏对于我的Google搜索和特定问题,此答案对我的帮助最大。
杰·基林

1
我认为这是答案而不是评论,这没有什么错。已投票。我认为拒绝投票的人只是在漫不经心地阅读(就像大多数问题很接近!; p一样)。
iconoclast

-1

在@szymon-lipiński(SzymonLipiński)给出的答案中,execute方法对我不起作用。它抛出了MySQL语法错误。

对我有用的MySQL语法是这个。

execute "ALTER TABLE mytable CHANGE `column_name` `column_name` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP"

因此,可以按照以下步骤为迁移脚本中的datetime列设置默认值:

def up
  create_table :foo do |t|
    t.datetime :starts_at, :null => false
  end

  execute "ALTER TABLE `foo` CHANGE `starts_at` `starts_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP"
end
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.