【问题标题】:rails migration using up/down to add and update existing records使用 up/down 来添加和更新现有记录的 rails 迁移
【发布时间】:2021-07-24 20:31:04
【问题描述】:

我必须在现有表中添加 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, :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 没有被执行还是我在迁移文件中做错了什么?(提前感谢您的帮助)

【问题讨论】:

    标签: ruby-on-rails rails-migrations


    【解决方案1】:

    down 在你运行rails db:rollback 时使用,这是你想要做的吗?

    up 在运行rails db:migrate 时使用。通常 Rails 知道要做什么,在迁移中使用 change 就足够了,但有时你需要使用 up/down,所以迁移是可逆的。


    来自docs

    您还可以使用 up 和 down 方法使用旧样式的迁移 而不是更改方法。 up 方法应该描述 您想对架构进行的转换,以及 down 方法 您的迁移应该恢复由 up 完成的转换 方法。换句话说,如果您 做一个向上,然后是一个向下。例如,如果您在 up 方法,你应该把它放在 down 方法中。

    【讨论】:

    • 如果大部分迁移适合def change,也可以使用reversible do |dir|\ndir.up do\n ...
    【解决方案2】:

    你需要这个:

    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 填充,它们没有数据库配置的默认值。分配的值是动态的,您只能在架构中放置固定的默认值。

    当您使用timestamps 方法指定created_at 时,会自动添加null: false(从Rails 5 开始)。当您显式创建/删除 created_at/updated_at 列时,该选项不会自动添加。

    【讨论】:

    • 抱歉,也许我解释得不够清楚,我并不是说我希望看到这些字段的默认日期值。我的意思是我没有看到 schema.rb 文件中反映的not null 约束
    • 在数据库中没有not null 约束,b/c ActiveRecord 处理这个约束,而不是数据库。
    • 哦,我刚刚注意到 Rails 5 确实在时间戳中添加了null: false。所以我猜你的期望应该是正确的!
    • @juan_code18 我更改了回复以解决我对您的问题以及 ActiveRecord 工作原理的误解
    【解决方案3】:

    听起来你在寻找这样的东西。

     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)
    

    看看change_column_null

    【讨论】:

    • 所以这个答案对我来说不太管用,因为正如我所说,我的数据库中目前有冲突记录
    【解决方案4】:

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

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

    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
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-08-29
      • 1970-01-01
      • 2021-08-25
      • 1970-01-01
      • 2014-05-07
      • 2023-01-13
      • 1970-01-01
      相关资源
      最近更新 更多