我必须向现有表添加 2 个新列,它们是
created_at
、updated_at
。但是, created_at
和 updated_at
字段不应为空,除非我的数据库中有多个现有记录,因此简单的 change
迁移不起作用。我决定进行向上/向下迁移,因为我需要先更新这些记录,然后再将这些字段设置为not null
。
我的迁移文件如下所示:
Class AddColumnsToCharge < ActiveRecord::Migration[6.0]
def up
add_column :charges, :created_at, :datetime
add_column :charges, :updated_at, :datetime
Charge.update_all(created_at: Time.now, updated_at: Time.now)
end
def down
change_column :charges, :created_at, :datetime, null:false
change_column :charges, :updated_at, :datetime, null:false
end
end
现在这似乎有效,因为我不再收到错误抱怨现有列的
created_at
/updated_at
字段为空值。但是我的 schema.rb 没有将这些字段显示为 null: false
相反,我看到了
create_table "charges", force: :cascade do |t|
t.bigint 'amount'
t.datetime 'created_at'
t.datetime 'updated_at'
我期望看到的是:
create_table "charges", force: :cascade do |t|
t.bigint 'amount'
t.datetime 'created_at', null: false
t.datetime 'updated_at', null: false
我的down没有被执行还是我在迁移文件中做错了什么?(感谢您提前提供的帮助)
down
时使用
rails db:rollback
,这是你想要做的吗?
up
在运行rails db:migrate
时使用。通常 Rails 知道要做什么,因此在迁移中使用 change
就足够了,但有时您需要使用 up/down
以便迁移是可逆的。
来自文档
您还可以使用旧式的迁移方式,使用向上和向下的方法 而不是改变方法。 up 方法应该描述 您想要对模式进行的转换以及 down 方法 您的迁移应该恢复由 up 完成的转换 方法。换句话说,如果您 先向上,然后向下。例如,如果您在 up 方法,你应该把它放到 down 方法中。
你需要这个:
class AddTimestamps < ActiveRecord::Migration[6.1]
def change
reversible do |dir|
change_table :charges do |t|
dir.up do
t.timestamps
Charge.update_all(created_at: Time.now, updated_at: Time.now)
end
dir.down do
remove_column :charges, :created_at
remove_column :charges, :updated_at
end
end
end
end
end
created_at
和updated_at
的默认值未反映在架构中,b/c它始终由ActiveRecord填充,它们没有数据库配置的默认值。分配的值是动态的,您只能在架构中放置固定的默认值。
当您使用
null: false
方法指定 created_at
时,会自动添加 timestamps
(Rails 5 及以上版本)。当您显式创建/删除created_at/updated_at列时,不会自动添加该选项。
在我看来,你正在寻找这样的东西。
add_column :charges, :created_at, :datetime
add_column :charges, :updated_at, :datetime
change_column_null(:charges, :created_at, false, Time.now)
change_column_null(:charges, :updated_at, false, Time.now)
这是使用
up
和 down
的一个很好的示例(来自关于迁移的 Rails Guide:
class ChangeProductsPrice < ActiveRecord::Migration[7.0]
def up
change_table :products do |t|
t.change :price, :string
end
end
def down
change_table :products do |t|
t.change :price, :integer
end
end
end
我遇到了类似的问题,我找到了一个我认为也适合您的解决方案。我很确定您不需要这个,但对于未来的人来说它可能会有用。因此,这里的想法是首先添加时间戳,但可能为 null (null: true),然后我们使用当前的 Time.zone 更新这些记录,使它们不为 null,然后我们为每个字段设置 null: false。我将发送我的代码示例,您应该用您的表格名称替换表格
class AddTimetableViews < ActiveRecord::Migration[7.0]
def change
add_timestamps :table1, null: true
Table1.where(created_at: nil).update_all(created_at: Time.zone.now, updated_at: Time.zone.now)
change_column_null :table1, :created_at, false
change_column_null :table1, :updated_at, false
end
end
这样,数据库中的当前记录将被更新,之后架构将如下所示:
create_table 'table1', force: :cascade do |t|
...
t.datetime 'created_at', null: false
t.datetime 'updated_at', null: false
end
另一种更短的方法可能是
class AddTimestampsToTable1AndTable2 < ActiveRecord::Migration[6.0]
def change
add_timestamps :table1, default: -> { 'CURRENT_TIMESTAMP' }, null: false
add_timestamps :table2, default: -> { 'CURRENT_TIMESTAMP' }, null: false
end
end
这种方式也有效,但模式看起来会有点不同,看起来像这样:
create_table 'table1', force: :cascade do |t|
...
t.datetime 'created_at', default: -> { 'CURRENT_TIMESTAMP' }, null: false
t.datetime 'updated_at', default: -> { 'CURRENT_TIMESTAMP' }, null: false
end
希望这有效。有任何问题欢迎询问
我能够通过运行两个不同的迁移来长期解决我的问题。
我的第一次迁移添加了新列:
Class AddColumnsToCharge < ActiveRecord::Migration[6.0]
def change
add_column :charges, :created_at, :datetime
add_column :charges, :updated_at, :datetime
end
end
在运行第二次迁移之前,我进入了 Rails 控制台并更新了当前记录:
Charge.update_all(created_at: Time.now, updated_at: Time.now)
第二次迁移:
Class AddColumnsToCharge < ActiveRecord::Migration[6.0]
def change
change_column :charges, :created_at, :datetime, null:false
change_column :charges, :updated_at, :datetime, null:false
end
end
这个答案没有回答最初的问题,但完成了我最终想要的工作。