我在以前的迁移中创建了一个日期列,并将其设置为可空。现在我想把它改成非空。假设数据库中有空行,我该怎么做呢?我可以把这些列设置为时间。现在如果它们是空的。


当前回答

如果你有现有的记录,你不能使用add_timestamps和null:false,所以这里是解决方案:

def change
  add_timestamps(:buttons, null: true)

  Button.find_each { |b| b.update(created_at: Time.zone.now, updated_at: Time.zone.now) }

  change_column_null(:buttons, :created_at, false)
  change_column_null(:buttons, :updated_at, false)
end

其他回答

如果你有现有的记录,你不能使用add_timestamps和null:false,所以这里是解决方案:

def change
  add_timestamps(:buttons, null: true)

  Button.find_each { |b| b.update(created_at: Time.zone.now, updated_at: Time.zone.now) }

  change_column_null(:buttons, :created_at, false)
  change_column_null(:buttons, :updated_at, false)
end

Rails 4:

def change
  change_column_null(:users, :admin, false )
end

如果你在迁移中这样做,那么你可能会这样做:

# Make sure no null value exist
MyModel.where(date_column: nil).update_all(date_column: Time.now)

# Change the column to not allow null
change_column :my_models, :date_column, :datetime, null: false

在Rails 4.02+中,根据文档,没有像update_all这样带有2个参数的方法。相反,你可以使用下面的代码:

# Make sure no null value exist
MyModel.where(date_column: nil).update_all(date_column: Time.now)

# Change the column to not allow null
change_column :my_models, :date_column, :datetime, null: false

根据强迁移gem,在生产中使用change_column_null是一个坏主意,因为它在检查所有记录时阻塞了读写。

处理这些迁移(特定于Postgres)的推荐方法是将此过程分为两个迁移。

一是用约束修改表:

class SetSomeColumnNotNull < ActiveRecord::Migration[6.0]
  def change
    safety_assured do
      execute 'ALTER TABLE "users" ADD CONSTRAINT "users_some_column_null" CHECK ("some_column" IS NOT NULL) NOT VALID'
    end
  end
end

和一个单独的迁移来验证它:

class ValidateSomeColumnNotNull < ActiveRecord::Migration[6.0]
  def change
    safety_assured do
      execute 'ALTER TABLE "users" VALIDATE CONSTRAINT "users_some_column_null"'
    end
  end
end

上面的示例是从链接的文档中提取的(并略有修改)。显然,对于Postgres 12+,你也可以在模式中添加NOT NULL,然后在验证运行后删除约束:

class ValidateSomeColumnNotNull < ActiveRecord::Migration[6.0]
  def change
    safety_assured do
      execute 'ALTER TABLE "users" VALIDATE CONSTRAINT "users_some_column_null"'
    end

    # in Postgres 12+, you can then safely set NOT NULL on the column
    change_column_null :users, :some_column, false
    safety_assured do
      execute 'ALTER TABLE "users" DROP CONSTRAINT "users_some_column_null"'
    end
  end
end

当然,这意味着对于Postgres的早期版本,您的模式将不会显示列为not NULL,因此我还建议设置一个模型级验证,要求显示该值(尽管我建议即使对于允许此步骤的PG版本也是如此)。

此外,在运行这些迁移之前,您需要用一个值而不是null更新所有现有记录,并确保任何写入表的生产代码都没有将值写入null。