rails 迁移使用向上/向下添加和更新现有记录

问题描述 投票:0回答:6

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

ruby-on-rails rails-migrations
6个回答
3
投票
当你运行

down

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

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


来自文档

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


1
投票

你需要这个:

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列时,不会自动添加该选项。


0
投票

在我看来,你正在寻找这样的东西。

 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


0
投票

这是使用

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

0
投票

我遇到了类似的问题,我找到了一个我认为也适合您的解决方案。我很确定您不需要这个,但对于未来的人来说它可能会有用。因此,这里的想法是首先添加时间戳,但可能为 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

希望这有效。有任何问题欢迎询问


-1
投票

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

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

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

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

© www.soinside.com 2019 - 2024. All rights reserved.