当我 运行 在数据迁移之前使用 ActiveRecord 进行架构迁移时,数据无法在数据库中正确更新

When I run a schema migration before a data migration, with ActiveRecord, data does not properly update in DB

截至目前,我有一个用户 table 有列 id, name, email, status

status 字段是整数类型,值为 1 和 2 分别代表活跃和不活跃的用户。

我想将status字段改为字符串类型并迁移数据 -- 将1转换为"Active",将2转换为"Inactive"

我用 rails g migration

生成了 2 个迁移文件

user.rb

class User < ApplicationRecord
  module Status
    ACTIVE = 'Active'.freeze
    INACTIVE = 'Inactive'.freeze

    ALL = [ACTIVE, INACTIVE].freeze
  end

  validates :status, presence: true
  validates :status, inclusion: Status::ALL
end

db/migrate/20190906115523_update_user_status_type.rb

def UpdateUserStatusType < ActiveRecord::Migration[5.2]
  def up
    change_column :users, :status, :string, default: User::Status::ACTIVE, 
  end

  def down
    User.where(status: User::Status::ACTIVE).update_all(status: 1)
    User.where(status: User::Status::INACTIVE).update_all(status: 2)
    change_column :users, :status, :integer, default: 1
  end
end

db/migrate/20190906115828_update_user_statuses.rb

def UpdateUserStatuses < ActiveRecord::Migration[5.2]
  def data
    User.where(status: 1).update_all(status: User::Status::ACTIVE)
    User.where(status: 2).update_all(status: User::Status::INACTIVE)  
  end
end

运行之后rails db:migrate

Expected: 迁移完成后,每个用户的状态应转换为 "Active" 或 "Inactive"。

Actual Results:每个用户的状态都转换为"0"字符串类型。

您假设在第一次迁移运行后 (change_column :users, :status, :string, default: User::Status::ACTIVE) 您仍然可以从 status 列中获取旧值,但事实并非如此。当您将该列的类型更改为字符串时,所有整数值都是无效的,因此我怀疑您的数据库只是将所有无效值更改为“0”。

如果我被告知要对生产中大量使用的应用程序进行此更改,我会在几个单独的拉取中推出此更改 requests/migrations。我将创建一个全新的单独列,遍历所有用户,根据旧列中的值设置新列的值,然后删除旧列。这是进行此更改的更安全的方法。