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

Har*_*tty 105 ruby-on-rails

考虑下面的表创建脚本:

create_table :foo do |t|
  t.datetime :starts_at, :null => false
end
Run Code Online (Sandbox Code Playgroud)

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

我试图在rails中找到DB独立的等价物,用于下面给出的SQL列定义:

Oracle语法

start_at DATE DEFAULT SYSDATE() 
Run Code Online (Sandbox Code Playgroud)

MySQL语法

start_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
Run Code Online (Sandbox Code Playgroud)

要么

start_at DATETIME DEFAULT NOW()
Run Code Online (Sandbox Code Playgroud)

Wil*_*ill 153

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

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

  • 很好的答案.纯粹地说,请注意,在Postgres中,CURRENT_TIMESTAMP将是当前事务开始的时间,因此在同一事务中创建的多个记录将获得相同的值.如果你想要语句执行的实际当前时间(忽略事务上下文),请查看`CLOCK_TIMESTAMP`. (11认同)
  • +10000这是现在更相关的答案. (10认同)

Szy*_*ski 115

您可以在模型中添加如下函数:

  before_create :set_foo_to_now
  def set_foo_to_now
    self.foo = Time.now
  end
Run Code Online (Sandbox Code Playgroud)

这样模型就可以在模型中设置当前时间.

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

execute 'alter table foo alter column starts_at set default now()'
Run Code Online (Sandbox Code Playgroud)

设置这样的东西:

create_table :foo do |t|
  t.datetime :starts_at, :null => false, :default => Time.now
end
Run Code Online (Sandbox Code Playgroud)

导致在迁移期间执行Time.now函数,然后创建数据库中的表,如下所示:

create table foo ( starts_at timestamp not null default '2009-01-01 00:00:00');
Run Code Online (Sandbox Code Playgroud)

但我认为这不是你想要的.

  • 请注意**before_create**回调在插入数据库之前运行,但是在实例化对象之后(比如`new()`).所以`self.foo = Time.now`将覆盖你可能赋予`new()`的值.我建议`self.foo = Time.current,除非self.foo.present?`. (9认同)
  • 不是这样,当使用execute时,这将在schema.rb中放置一行`:starts_at,:default =>'now()'`.但是当使用`rake db:schema:dump`来覆盖schema.rb并使用`:starts_at,:default =>'2015-05-29 09:46:33'`(或任何日期)时,这将不起作用当你启动脚本时)......伤心.... (2认同)

Jim*_*Jim 12

如果您有一个名为created_at或的日期时间列created_on,ActiveRecord将"神奇地"将其设置为创建日期时间.除了拥有该列之外,您不需要执行任何其他操作.

您还可以拥有updated_atupdated_on更新记录时更新.


Gio*_*tto 9

我正在寻找类似的解决方案但我最后使用https://github.com/FooBarWidget/default_value_for.

default_value_for插件允许以声明方式定义ActiveRecord模型的默认值.例如:

class User < ActiveRecord::Base
  default_value_for :name, "(no name)"
  default_value_for :last_seen do
    Time.now
  end
end

u = User.new
u.name       # => "(no name)"
u.last_seen  # => Mon Sep 22 17:28:38 +0200 2008
Run Code Online (Sandbox Code Playgroud)


Art*_*ero 9

我经常这样做:

def change
  execute("
    ALTER TABLE your_table
    ALTER COLUMN your_column
    SET DEFAULT CURRENT_TIMESTAMP
  ")
end
Run Code Online (Sandbox Code Playgroud)

所以,你schema.rb会有类似的东西:

create_table "your_table", force: :cascade do |t|
  t.datetime "your_column", default: "now()"
end
Run Code Online (Sandbox Code Playgroud)


Mat*_*ong 7

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

class MakeStartsAtDefaultDateForFoo < ActiveRecord::Migration[5.0]
  def change
    change_column :foos, :starts_at, :datetime, default: -> { 'CURRENT_TIMESTAMP' }
  end
end
Run Code Online (Sandbox Code Playgroud)

  • FWIW我现在刚刚使用了这种语法,并感谢我的Google搜索和特定问题对我的帮助最大。 (3认同)
  • 投票否决某些内容而不解释答案所涉及的问题是不好的形式。有人知道为什么这被否决吗?如果您想更改列而不是创建列,它会显示语法。 (2认同)
  • @nurettin我明白您的意思,但是在我的辩护中,“创建”新列的语法与“更改”现有列的语法稍有不同。为那些试图通过网络搜索找到问题的人提供这种语法,同时尝试将默认值添加到当前数据模型中,而不是完全创建新的模型/表,这可能会很有帮助。没有?您假设每个人都知道他们可以为change_column使用相同的lambda。也许他们应该意识到这一点,但这就是我在这里回答的原因-因此,他们不必去其他任何地方就可以解决这一问题。干杯! (2认同)