How to change a nullable column to not nullable in a Rails migration?
我在之前的迁移中创建了一个日期列,并将其设置为可为空。 现在我想把它改成不可空。 假设该数据库中有空行,我该怎么做呢? 如果它们当前为空,我可以将这些列设置为Time.now。
如果你在迁移中执行它,那么你可能会这样做:
1 2 3 4 5
| # 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 |
-
请注意,因为这让我破坏了我的开发数据库。而是使用显式哈希语法,如下所示:MyModel.update_all({:date_column => Time.now}, {:date_column => nil})。原始表单中的查询只是使我的所有模型在该字段中都没有值。
-
感谢更新。我知道当我写这个答案时并非如此,但我不记得当时使用的是哪个版本的Ruby或RoR。
-
您是否在此迁移中使用了"向上"/"向下"方法,或者您可以在迁移中使用简单的更改方法吗?
-
change方法不适用于这种情况,因为(1)update_all方法将在迁移和潜在恢复上执行。这可能不是最糟糕的事情,但是因为(2)迁移无法知道在潜在的恢复中列的变化。所以对于这种情况,我会坚持使用up和down。
-
我可以将MyModel.update_all({:date_column => Time.now}, {:date_column => nil})添加到迁移文件中吗?
-
RE @DanneManne - 在这种情况下,在向下时,date_column将全部具有NOT NULL值,因此update_all语句将不执行任何操作。所以change块很好。
-
RE @toobulkeh:是的,但我个人仍然会使用双重阻止。如果不是update_all,则为change_column语句。因为迁移在更改之前不知道该列的状态。那又怎么回事呢?
-
您永远不应该使用模式本身来在迁移中进行数据更改。例如,您可以在以后的迁移中删除模型(在本例中为MyModel)。如果有人会完成所有迁移,那么它就会破坏。而是直接使用ActiveRecord :: Base.connection.execute('你的sql here')直接执行SQL语句
-
MyModel.where({:date_column => nil})。update_all({:date_column => Time.now}) - Rails 4
-
对于任何感兴趣的人,我的答案显示了如何一步完成。
在Rails 4中,这是一个更好的(DRYer)解决方案:
1
| change_column_null :my_models, :date_column, false |
要确保该列中没有NULL值的记录,您可以传递第四个参数,该参数是用于具有NULL值的记录的默认值:
1
| change_column_null :my_models, :date_column, false, Time.now |
-
当表已具有空值时,这会导致问题。看我的回答
-
也可用于3.2。还有第4个参数用于设置默认值,其中value为null。
-
加1 change_column_null。然而,里克史密斯上面的评论指出了一个非常有效的案例。
-
已更新以添加用于更新空值的查询。第4个参数(默认值)仅在您确实希望为将来的记录设置默认值时才有用。
-
实际上,根据Rails 4.2文档,第4个参数不会为将来的记录设置默认值:"该方法接受一个可选的第四个参数来用现有的+ NULL + s替换其他值。请注意第四个参数未设置列的默认值。"
Rails 4(其他Rails 4答案有问题):
1 2 3 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,这似乎是最准确和完整的答案,包括已注释掉的默认设置。
-
如果要向表中添加新列并希望为null插入新值,但又不想为该列添加默认值,则可以在迁移中执行此操作:add_column :users, :admin, :string然后change_column_null(:admin, :string, false,"new_value_for_existing_records")
创建具有change_column语句且具有:default =>值的迁移。
1
| change_column :my_table, :my_column, :integer, :default => 0, :null => false |
请参阅:change_column
根据数据库引擎,您可能需要使用change_column_null
-
这对我有用。在本地使用MySql。当在Heroku(Postgres)中推送并运行应用程序时,当我将其写为空时,它会在非空的列上进行操作 - 这是正确的。只有"change_column_null"才能工作,不能在MySql上使用"change_column ...:null => false"。谢谢。
-
那么你在change_column_null之后的迁移是什么?
-
邮件比MySQL更严格 - 我希望它需要change_column_null。
-
@rtfminc我强烈建议您在开发和生产中使用相同的数据库引擎,因为它避免了边缘情况下的许多问题。
导轨4:
1 2 3
| def change
change_column_null(:users, :admin, false )
end |
在根据文档的Rails 4.02+中,没有类似update_all的方法,有2个参数。相反,可以使用此代码:
1 2 3 4 5
| # 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,因此这里是解决方案:
1 2 3 4 5 6 7 8
| 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 |