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

考虑下面的表创建脚本:

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

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

我正在尝试为下面给出的 SQL 列定义在 ails 中找到一个与 DB 无关的等价物:

Oracle 语法

start_at DATE DEFAULT SYSDATE()

MySQL 语法

start_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP

或者

start_at DATETIME DEFAULT NOW()
102311 次浏览

你可以像下面这样在模型中添加一个函数:

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

So that the model will set the current time in the model.

您还可以在迁移中放置一些 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');

但我觉得这不是你想要的。

如果表中有名为 created_at/created_onupdated_at/updated_on. Api.rubyonrails.org的字段,则 ActiveRecord 会自动创建和更新时间戳操作

You don't need to do anything else except to have that column.

我正在寻找一个类似的解决方案,但我结束了使用 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

In the answer given by @szymon-lipiński (Szymon Lipiński), the execute method didn't work for me. It was throwing a MySQL syntax error.

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

I usually do:

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

So, your schema.rb is going to have something like:

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

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

如果您需要在 Rails 5中创建 改变现有的日期时间列(而不是像其他答案中指定的那样创建一个新表) ,以便它能够利用默认的日期功能,您可以创建一个像下面这样的迁移:

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

你知道 除非你有一个默认的 updated_at/created_at,否则 upserts 会失败吗?

没有移植标志自动做到这一点,你必须手动包括一个选项对象与 default

create_table :table_foos do |t|
#...
# date with timestamp
t.datetime :last_something_at, null: false, default: -> { "CURRENT_TIMESTAMP" }
  

# standard timestamps
t.timestamps({default: -> { "CURRENT_TIMESTAMP" }})
end