如何在Rails迁移中将可空列更改为不可为空?

时间:2011-05-11 15:34:33

标签: ruby-on-rails migration

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

8 个答案:

答案 0 :(得分:184)

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

# 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

答案 1 :(得分:142)

在Rails 4中,这是一个更好的(DRYer)解决方案:

change_column_null :my_models, :date_column, false

为确保该列中不存在包含NULL值的记录,您可以传递第四个参数,该参数是用于具有NULL值的记录的默认值:

change_column_null :my_models, :date_column, false, Time.now

答案 2 :(得分:63)

Rails 4(其他Rails 4答案有问题):

def change
  change_column_null(:users, :admin, false, <put a default value here> )
  # change_column(:users, :admin, :string, :default => "")
end

将其中包含NULL值的列更改为不允许NULL将导致问题。这正是在开发设置中可以正常工作的代码类型,然后在您尝试将其部署到 LIVE 生产时崩溃。您应该首先将NULL值更改为有效的值,然后然后禁止NULL。 change_column_null中的第4个值就是这样。有关详细信息,请参阅documentation

另外,我通常更喜欢为字段设置默认值,因此每次创建新对象时都不需要指定字段的值。我也包含了注释掉的代码。

答案 3 :(得分:32)

创建一个change_column语句,其值为:default =>

change_column :my_table, :my_column, :integer, :default => 0, :null => false

请参阅:change_column

根据数据库引擎的不同,您可能需要使用change_column_null

答案 4 :(得分:11)

Rails 4:

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

答案 5 :(得分:3)

根据docs 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

答案 6 :(得分:1)

如果您有现有记录,则无法使用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

答案 7 :(得分:0)

Strong Migrations 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