使用 up/down 来添加和更新现有记录的 rails 迁移

问题描述

我必须向现有表添加 2 个新列,它们是 created_atupdated_at。但是,created_atupdated_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,:datetime,null:false
     change_column :charges,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 以便迁移是可逆的。


来自docs

您也可以使用 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_atupdated_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,false,Time.now)
 change_column_null(:charges,Time.now)

看看这个change_column_null

,

通过运行两个不同的迁移,我能够长期解决我的问题。

我的第一次迁移添加了新列:

Class AddColumnsToCharge < ActiveRecord::Migration[6.0]
   def change
     add_column :charges,:datetime
     add_column :charges,: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,:datetime,null:false
     change_column :charges,null:false
  end
end

这个答案没有回答最初的问题,而是完成了我最终想要的工作。