change_column_default 的可逆迁移,在 Rails 中没有任何默认值
Reversible migration for change_column_default from not having any default in Rails
Rails guides to active record migrations说你可以做到
change_column_default :products, :approved, from: true, to: false
我在 Rails 中有一个类似于以下内容的 change
方法:
change_column_default :people, :height, from: nil, to: 0
打算从没有任何默认值到默认值为零。
但是,当我尝试回滚时,我得到了
ActiveRecord::IrreversibleMigration: ActiveRecord::IrreversibleMigration
考虑到我给 Rails 一个 from
和 to
,为什么它不接受它?
我正在使用 Rails 4.2.0.
当您尝试将数据类型从 value
(0) 降级为 nil
时,rails 会报错。因为它可能会丢失数据
另一个例子是从 string
-> integer
.
这真的是good article explain the same
更新
您的反向迁移似乎在 command_recorder
中调用了 change_column_default
(activerecord-4.2.0/lib/active_record/migration/command_recorder.rb#76)
请检查您是否将 nil
设置为默认值。
更新2
事实证明,如果您在迁移中使用 change
,这些迁移必须是可逆的
但change_column 无法通过change
方法逆转。所以你将不得不使用旧的 rails 迁移方法与 up
和 down
阅读这篇文章解释 scenario
首先,正如 Jonathan Allard 所说,from
和 to
不在方法源代码中,这意味着 change_column_default
不接受它。就是这样:
def sum(a)
return a
end
现在,如果您尝试将两个变量传递给它,例如 sum(a, b)
或其他任何东西,它都不会接受该权利。这是您在上面使用 from
和 to
.
尝试执行的操作
现在正确的语法是:
change_column_default(:people, :height, 0)
该方法不接受 from
和 to
(因为它是这样定义的,即使它们是散列键,如果该方法不在任何地方使用该键值对,那么它没有用)并且如果它是一个新列,显然它将具有默认值 nil
(如果之前未设置)并假设列 height
如果类型为 integer
并且你给它默认值 a
它将存储 0
作为默认值(不是 100% 确定但是已经尝试从 rails 控制台这样做)。 rails 当前默认值是什么并不重要,它只需要新的默认值。所以如果当前默认值为0
,而你设置为nil
,rails将不会被投诉。这是您的数据库,您希望如何处理它。如果你做错了,比如将 string
分配给 boolean
,数据库会中断它,那么它显然会抛出错误。
现在,一旦此迁移具有 运行,那么它会将默认值设置为 0
,现在 rails 不知道以前的默认值是什么。因为它不见了,它没有把它存储在任何地方。所以这就是为什么 change_column_default
是不可逆的迁移。如果您尝试将其回滚,它会在 change
方法的情况下为您提供 ActiveRecord::IrreversibleMigration: ActiveRecord::IrreversibleMigration
。表示当您使用过:
def change
change_column_default(:people, :height, 0)
end
这就是为什么对于这种迁移我们使用方法 up
和 down
:
def up
change_column_default(:people, :height, 0)
end
def down
change_column_default(:people, :height, nil)
end
希望这对您有所帮助。
如果您使用 mysql 作为适配器,那么根据这个 link http://apidock.com/rails/ActiveRecord/ConnectionAdapters/AbstractMysqlAdapter/change_column_default,您的 change_column_default
迁移 运行 是这样的
def change_column_default(table_name, column_name, default) #:nodoc:
column = column_for(table_name, column_name)
change_column table_name, column_name, column.sql_type, :default => default
end
因此,正如您所见,当您调用 change_column_default
并根据此 link 时,它会在自身内部调用 change_column
http://edgeguides.rubyonrails.org/active_record_migrations.html
change_column
迁移是不可逆的。
这说明了为什么你会得到 ActiveRecord::IrreversibleMigration: ActiveRecord::IrreversibleMigration
因此,如果您想使用 change_column_default
运行 迁移,则必须添加 def up
和 def down
。
我建议使用 change_column,因为它已在 change_column_default 中被调用。
def up
change_column :people, :height, :integer, default: 0
end
def down
change_column :people, :height, :integer, default: nil
end
使用 from
和 to
在 Rails 5+
中是 added
问题中链接的指南适用于 Edge Rails,不适用于 Rails 的发布版本。
change_column_default
的可逆语法是拉取请求 20018 的结果。拉取请求还更新了 Edge Rails.
的 Rails 指南
来自activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
:
- def change_column_default(table_name, column_name, default)
+ # Passing a hash containing +:from+ and +:to+ will make this change
+ # reversible in migration:
+ #
+ # change_column_default(:posts, :state, from: nil, to: "draft")
+ #
+ def change_column_default(table_name, column_name, default_or_changes)
拉取请求于 2015 年 6 月 27 日发出,比 2015 年 8 月 1 日发布的任何 Rails 版本更新。
migration for Rails 4.2.3 的文档反映了可逆语法尚不可用的事实:
change_column_default :products, :approved, false
自 2016 年 10 月起,此功能(使用 to:
和 from:
使 change_column_default
可逆)现已在 5.x 分支上可用。不幸的是,它不可用 4.2.x 或更低版本。 :(
验证:git tag --contains f9c841927ac3d1daea2a9cebf08b18e844e5eec5
在 rails 项目中。
补充几点。如果您遇到不支持可逆 change_column_default 的 Rails 版本,解决该问题的一种选择是:
def change
# If your dataset is small, just cache in memory, if large, consider file dump here:
cache = Table.all
# Full column def important for reversibility
remove_column :table, :column, :type, { config_hash }
# Re-add the column with new default:
add_column :table, :column, :type, { config_hash, default: 0 }
# Now update the data with cached records (there might be more efficient ways, of course):
cache.each do |rec|
Table.find(rec.id).update(column: rec.column)
end
end
如果您对迁移文件有疑问,请将您的迁移更改为此格式。
change_column_default :table_name, :column_name, from: nil, to: "something"
Rails guides to active record migrations说你可以做到
change_column_default :products, :approved, from: true, to: false
我在 Rails 中有一个类似于以下内容的 change
方法:
change_column_default :people, :height, from: nil, to: 0
打算从没有任何默认值到默认值为零。
但是,当我尝试回滚时,我得到了
ActiveRecord::IrreversibleMigration: ActiveRecord::IrreversibleMigration
考虑到我给 Rails 一个 from
和 to
,为什么它不接受它?
我正在使用 Rails 4.2.0.
当您尝试将数据类型从 value
(0) 降级为 nil
时,rails 会报错。因为它可能会丢失数据
另一个例子是从 string
-> integer
.
这真的是good article explain the same
更新
您的反向迁移似乎在 command_recorder
中调用了 change_column_default
(activerecord-4.2.0/lib/active_record/migration/command_recorder.rb#76)
请检查您是否将 nil
设置为默认值。
更新2
事实证明,如果您在迁移中使用 change
,这些迁移必须是可逆的
但change_column 无法通过change
方法逆转。所以你将不得不使用旧的 rails 迁移方法与 up
和 down
阅读这篇文章解释 scenario
首先,正如 Jonathan Allard 所说,from
和 to
不在方法源代码中,这意味着 change_column_default
不接受它。就是这样:
def sum(a)
return a
end
现在,如果您尝试将两个变量传递给它,例如 sum(a, b)
或其他任何东西,它都不会接受该权利。这是您在上面使用 from
和 to
.
现在正确的语法是:
change_column_default(:people, :height, 0)
该方法不接受 from
和 to
(因为它是这样定义的,即使它们是散列键,如果该方法不在任何地方使用该键值对,那么它没有用)并且如果它是一个新列,显然它将具有默认值 nil
(如果之前未设置)并假设列 height
如果类型为 integer
并且你给它默认值 a
它将存储 0
作为默认值(不是 100% 确定但是已经尝试从 rails 控制台这样做)。 rails 当前默认值是什么并不重要,它只需要新的默认值。所以如果当前默认值为0
,而你设置为nil
,rails将不会被投诉。这是您的数据库,您希望如何处理它。如果你做错了,比如将 string
分配给 boolean
,数据库会中断它,那么它显然会抛出错误。
现在,一旦此迁移具有 运行,那么它会将默认值设置为 0
,现在 rails 不知道以前的默认值是什么。因为它不见了,它没有把它存储在任何地方。所以这就是为什么 change_column_default
是不可逆的迁移。如果您尝试将其回滚,它会在 change
方法的情况下为您提供 ActiveRecord::IrreversibleMigration: ActiveRecord::IrreversibleMigration
。表示当您使用过:
def change
change_column_default(:people, :height, 0)
end
这就是为什么对于这种迁移我们使用方法 up
和 down
:
def up
change_column_default(:people, :height, 0)
end
def down
change_column_default(:people, :height, nil)
end
希望这对您有所帮助。
如果您使用 mysql 作为适配器,那么根据这个 link http://apidock.com/rails/ActiveRecord/ConnectionAdapters/AbstractMysqlAdapter/change_column_default,您的 change_column_default
迁移 运行 是这样的
def change_column_default(table_name, column_name, default) #:nodoc:
column = column_for(table_name, column_name)
change_column table_name, column_name, column.sql_type, :default => default
end
因此,正如您所见,当您调用 change_column_default
并根据此 link 时,它会在自身内部调用 change_column
http://edgeguides.rubyonrails.org/active_record_migrations.html
change_column
迁移是不可逆的。
这说明了为什么你会得到 ActiveRecord::IrreversibleMigration: ActiveRecord::IrreversibleMigration
因此,如果您想使用 change_column_default
运行 迁移,则必须添加 def up
和 def down
。
我建议使用 change_column,因为它已在 change_column_default 中被调用。
def up
change_column :people, :height, :integer, default: 0
end
def down
change_column :people, :height, :integer, default: nil
end
使用 from
和 to
在 Rails 5+
中是 added
问题中链接的指南适用于 Edge Rails,不适用于 Rails 的发布版本。
change_column_default
的可逆语法是拉取请求 20018 的结果。拉取请求还更新了 Edge Rails.
来自activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
:
- def change_column_default(table_name, column_name, default)
+ # Passing a hash containing +:from+ and +:to+ will make this change
+ # reversible in migration:
+ #
+ # change_column_default(:posts, :state, from: nil, to: "draft")
+ #
+ def change_column_default(table_name, column_name, default_or_changes)
拉取请求于 2015 年 6 月 27 日发出,比 2015 年 8 月 1 日发布的任何 Rails 版本更新。
migration for Rails 4.2.3 的文档反映了可逆语法尚不可用的事实:
change_column_default :products, :approved, false
自 2016 年 10 月起,此功能(使用 to:
和 from:
使 change_column_default
可逆)现已在 5.x 分支上可用。不幸的是,它不可用 4.2.x 或更低版本。 :(
验证:git tag --contains f9c841927ac3d1daea2a9cebf08b18e844e5eec5
在 rails 项目中。
补充几点。如果您遇到不支持可逆 change_column_default 的 Rails 版本,解决该问题的一种选择是:
def change
# If your dataset is small, just cache in memory, if large, consider file dump here:
cache = Table.all
# Full column def important for reversibility
remove_column :table, :column, :type, { config_hash }
# Re-add the column with new default:
add_column :table, :column, :type, { config_hash, default: 0 }
# Now update the data with cached records (there might be more efficient ways, of course):
cache.each do |rec|
Table.find(rec.id).update(column: rec.column)
end
end
如果您对迁移文件有疑问,请将您的迁移更改为此格式。
change_column_default :table_name, :column_name, from: nil, to: "something"