change_column_default的可逆迁移在Rails中没有任何默认值
Rails指向活动记录迁移说明你可以做到
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提供了一个,为什么不接受它呢?
我正在使用Rails 4.2.0。
如果您使用mysql作为适配器,那么根据此链接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
并根据此链接调用change_column_default
, http: change_column
migration是不可逆转的。
这显示了为什么你得到ActiveRecord::IrreversibleMigration: ActiveRecord::IrreversibleMigration
因此,如果您想使用change_column_default
运行迁移, 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
链接到问题的指南是针对Edge Rails的,而不是针对Rails的发布版本。
change_column_default
可逆语法是拉取请求20018的结果。 pull请求还更新了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都要新。
Rails 4.2.3的迁移文档反映了可逆语法尚不可用的事实:
change_column_default :products, :approved, false
首先, 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 console)。 轨道当前默认值是什么并不重要,它只需要新的默认值。 因此,如果当前默认值为0
并且您将其设置为nil
rails将不会投诉。 它是您的数据库,您希望如何处理它。 正如数据库中断它,如果你做错了,比如将string
赋值给boolean
那么它显然会抛出错误。
现在,一旦迁移已经运行,那么它将默认值设置为0
现在rails不知道先前的默认值是什么。 因为它消失了,它没有存储在任何地方。 这就是为什么change_column_default
是不可逆转的迁移。 如果你试图回滚它,它会给你ActiveRecord::IrreversibleMigration: ActiveRecord::IrreversibleMigration
以防change
方法。 意味着您使用过:
def change change_column_default(:people, :height, 0) end
这就是为什么对于这种迁移,我们使用down
方法:
def up change_column_default(:people, :height, 0) end def down change_column_default(:people, :height, nil) end
希望这可以帮助。
截至2016年10月,此function( to:
和from:
for change_column_default
可逆)现在可在5.x分支上使用。 不幸的是,它不是4.2.x或更低版本。 🙁
validation: git tag --contains f9c841927ac3d1daea2a9cebf08b18e844e5eec5
在rails项目中git tag --contains f9c841927ac3d1daea2a9cebf08b18e844e5eec5
。
只是补充几点。 如果你坚持使用不支持可逆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
当您尝试降级数据类型时,在这种情况下, value
从(0)变为nil
,rails会报告此错误。 因为它可能会丢失数据
另一个例子是从string
– > integer
。
这是一篇非常好的文章解释相同
UPDATE
您的反向迁移似乎在command_recorder
调用change_column_default
(activerecord-4.2.0/lib/active_record/migration/command_recorder.rb#76)
请检查是否将nil
设置为默认值。
UPDATE2
事实certificate,如果你在迁移中使用change
,那些必须能够可逆
但是change_column通过change
方法是不可逆的。 因此,您必须使用旧的rails迁移方法down
阅读本文解释方案