我在以前的迁移中创建了一个日期列,并将其设置为可空。现在我想把它改成非空。假设数据库中有空行,我该怎么做呢?我可以把这些列设置为时间。现在如果它们是空的。
创建一个迁移,它有一个change_column语句,值为:default =>。
change_column :my_table, :my_column, :integer, :default => 0, :null => false
看:change_column
根据数据库引擎的不同,您可能需要使用change_column_null
如果你在迁移中这样做,那么你可能会这样做:
# 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中,这是一个更好的(dry)解决方案:
change_column_null :my_models, :date_column, false
为了确保该列中不存在NULL值的记录,您可以传递第四个参数,这是用于NULL值记录的默认值:
change_column_null :my_models, :date_column, false, Time.now
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个值正是这样做的。有关详细信息,请参阅文档。
此外,我通常更喜欢为字段设置默认值,这样我就不需要每次创建新对象时都指定字段的值。我还包含了注释掉的代码来实现这一点。
在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
如果你有现有的记录,你不能使用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
根据强迁移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。
推荐文章
- Ruby:包含的反义词是什么?Ruby数组?
- 想要在Rails中找到没有关联记录的记录
- helper和helper_method做什么?
- 如何回滚只是一个步骤使用耙db:迁移
- 如何传递参数到一个Rake任务与环境在Rails?
- 如何在Rails中计算相对时间?
- 如何在Rails迁移中将可空列更改为不可空列?
- Ruby on Rails -从CSV文件导入数据
- Ruby/Rails:将Date转换为UNIX时间戳
- 如何从URL下载文件并保存在Rails中?
- Rails:缺少要链接的主机!请提供:host参数或设置default_url_options[:host]
- Rails。env vs RAILS_ENV
- 如何从Ruby数组中创建平均值?
- 实体框架回滚并移除不良迁移
- 我能在视图中得到当前控制器的名称吗?