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


188

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

Answers:


204

如果您在迁移中执行此操作,则可能会这样:

# 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
请注意,因为这使我破坏了开发数据库。而是使用显式哈希语法,例如:MyModel.update_all({:date_column => Time.now}, {:date_column => nil})。以您原始形式进行的查询仅使我所有的模型在该字段中都没有值。
dimitarvp 2012年

感谢更新。我知道写答案时不是这种情况,但是我不记得当时使用的是哪个版本的Ruby或RoR。
DanneManne 2012年

1
您是否在此迁移中使用“向上” /“向下”方法,或者可以在迁移中使用简单的更改方法?
EE33

2
change方法不太适合这种情况,因为(1)该update_all方法将同时在迁移和潜在还原上执行。那可能不是最坏的事情,而是因为(2)迁移无法得知潜在还原中列的更改。因此,在这种情况下,我会坚持使用updown
DanneManne

2
对于任何有兴趣的人,我的答案都说明了如何一步完成。
里克·史密斯

166

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

change_column_null :my_models, :date_column, false

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

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

4
当表已经具有空值时,这将导致问题。 看看我的回答
里克·史密斯

5
在3.2中也可用。也有第四个参数,用于设置默认值为null的位置。
toxaq 2014年

1
加1 change_column_null。但是,里克·史密斯(Rick Smith)的上述评论指出了一个非常有效的案例。
0112年

已更新以添加查询以更新空值。第四个参数(默认值)仅在您实际上还希望为将来的记录设置默认值时才有用。
mrbrdo

3
实际上,根据Rails 4.2文档,第4个参数未为将来的记录设置默认值:“该方法接受可选的第四个参数以将现有+ NULL +替换为其他值。请注意,第四个参数未设置列的默认值。”
Mike Fischer

70

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。的第4个值change_column_null正是这样做的。查看文件更多详细信息,。

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


3
对于Rails 4,这似乎是最准确,最完整的答案,包括注释掉的默认设置。
Mike Fischer 2015年

4
如果要向表中添加新列,并想为null插入新值,但又不想为该列添加默认值,则可以在迁移中执行此操作: add_column :users, :admin, :string然后change_column_null(:admin, :string, false, "new_value_for_existing_records")
colsen

34

创建一个change_column包含带有:default =>值的语句的迁移。

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

请参阅:change_column

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


1
这对我有用。在本地使用MySql。当在Heroku(Postgres)中推送并运行应用程序时,当我将其编写为null时,它就排在不为null的列上-正确地如此。在MySql上,只有“ change_column_null”不能使用“ change_column ...:null => false”。谢谢。
rtfminc 2011年

1
那么,什么是后change_column_null迁移
js111

1
Postges比MySQL更严格-我希望它会要求change_column_null
jessecurry 2012年

3
@rtfminc我强烈建议您在开发和生产中使用相同的数据库引擎,因为它避免了遇到极端情况时的许多问题。
yagooar


3

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

# 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

2

如果您已有记录,则不能使用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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.