Rails从遗留数据库迁移并将虚假数据库字段转换为默认值

时间:2016-10-29 18:42:05

标签: ruby-on-rails postgresql rails-activerecord

我正在对Rails中的应用程序进行全面的重建。旧版本(我们称之为“Legacy”)允许存储数据以便解释为虚假和真实。我不再希望以这种方式解释这些领域。

我为新应用程序编写了一个自定义Rake任务,用于从旧版数据库中虹吸数据并将该数据插入到新的数据库模型中。

问题源于布尔列我从PG中的Legacy DB模型迁移到新PG DB中的新模型。

假设这两个模型如下所示:

OLD MODEL

create_table "table_name", force: :cascade do |t|
    t.string    "name"
    t.boolean   "is_alive"
end

新模型

create_table "new_table_name", force: :cascade do |t|
    t.string  "new_name"
    t.boolean "is_dead",    default: false, null: false
end

让我们说我的自定义rake任务看起来类似于以下内容:

namespace :db do
  desc "Migrate old records to new model."
  task :migrate_old_records_to_new_model, [] => :environment do

  Model.find_each do |oldness|
    NewModel.find_or_initialize_by(oldness.name) do |newness|
      puts "\n\tCreating new updated record for #{oldness.name}"

      newness.new_name    = oldness.name
      newness.is_dead     = oldness.is_alive
    end
  end

当我尝试从Legacy DB进行转换时,我在布尔列上得到PG::NotNullViolation

1 个答案:

答案 0 :(得分:1)

最简单的方法是:

newness.is_dead     = !oldness.is_alive

表定义中的null: false告诉PG该字段永远不能为空。遗留数据似乎包含空值。只需将它们分配给新模型就会破坏数据库约束。

我知道!你在考虑"但是我告诉PG,默认值是false!"是。真正。但是,ActiveRecord 显式告诉PG将该字段设置为NULL并且PG符合,忽略默认值...但不是约束!如果要从insert语句中省略字段/值,则将使用隐含的默认值而不是将字段保留为NULL

更好的解决方案是在新模型中添加一些before_save验证,以设置这些默认值以避免将来出现类似情况。

你会这样做:

class NewModel < ActiveRecord::Base
  before_save :ensure_is_dead_is_not_null

  def ensure_is_dead_is_not_null
    is_dead = false if is_dead.nil?
    true
  end
end

我们需要让此before_save回调(ensure_is_dead_is_not_null)始终返回true,因为:

  

如果before_ *回调返回false,则取消所有后续回调和相关操作。

参考:http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html

此外,根据您处理价值分配和模型实施的方式,您可能希望在after_initialize而不是before_save上设置默认值。如果这样做,请记住每次Ruby初始化和实例(每次从数据库加载记录时)after_initialize,因此您需要确保不要盲目地破坏从DB加载的有效值。