何时可以在迁移的 self.down 方法中引发 ActiveRecord::IrreversibleMigration 异常?您应该在什么时候努力实际实施迁移的逆向?
7 回答
如果您正在处理生产级系统,那么是的,这非常糟糕。如果这是您自己的宠物项目,那么任何事情都是允许的(如果没有别的,这将是一种学习体验 :) 尽管迟早,即使在宠物项目中,您也会发现自己已经跨越了反向迁移只是必须在几天后撤消该迁移,无论是通过rake
还是手动。)
在生产场景中,您应该始终努力编写和测试可逆迁移,以防万一您在生产中经历它,然后发现一个迫使您回滚(代码和架构)到某个先前版本(待定)的错误一些重要的修复——以及其他无法使用的生产系统。)
反向迁移的范围从大多数微不足道的(删除迁移期间添加的列或表,和/或更改列类型等)到涉及更多的(execute
edJOIN
或INSERT
s UPDATE
),但没有什么比证明“清扫它”更复杂在地毯下”。如果不出意外,强迫自己思考实现反向迁移的方法可以让您对正向迁移正在解决的问题有新的认识。
您可能偶尔会遇到向前迁移删除功能的情况,从而导致数据从数据库中丢弃。 由于显而易见的原因,反向迁移无法恢复丢弃的数据。 尽管在这种情况下,可以建议让前向迁移自动保存数据或在可能发生回滚时将其保留,作为彻底失败的替代方案(保存到yml
、复制/移动到特殊表等),但您不要不必这样做,因为测试这种自动化过程所需的时间可能会超过手动恢复数据所需的时间(如果需要的话。) 但即使在这种情况下,您也可以随时进行反向迁移,而不是失败有条件地和暂时地等待某些用户操作失败(即测试是否存在必须手动恢复的某些必需表;如果丢失,则输出“我失败了,因为我无法XYZ
从虚无中重新创建表;XYZ
从备份手动恢复表然后再次运行我,我不会让你失望的!”)
如果要破坏数据,可以先对其进行备份。例如
def self.up
# create a backup table before destroying data
execute %Q[create table backup_users select * from users]
remove_column :users, :timezone
end
def self.down
add_column :users, :timezone, :string
execute %Q[update users U left join backup_users B on (B.id=U.id) set U.timezone = B.timezone]
execute %Q[drop table backup_users]
end
在生产场景中,您应该始终努力编写和测试可逆迁移,以防万一您在生产中经历它,然后发现一个迫使您回滚(代码和架构)到某个先前版本(待定)的错误一些重要的修复——以及其他无法使用的生产系统。)
进行可逆迁移对于开发和暂存来说是很好的,但假设代码经过良好测试,那么您想要在生产中向下迁移应该是非常罕见的。我在我的迁移中构建了生产模式下的自动 IrreversibleMigration。如果我真的需要撤消更改,我可以使用另一个“向上”迁移或删除异常。不过,这似乎很粗略。任何会导致这种可怕情况的错误都表明 QA 流程被严重搞砸了。
感觉你需要一个不可逆转的迁移可能表明你有更大的问题迫在眉睫。也许一些细节会有所帮助?
至于你的第二个问题:我总是“努力”写迁移的逆向。当然,我不是真的写的.down
,TextMate在创建的时候会自动插入.up
。
可逆数据迁移使使用 yaml 文件创建可逆数据迁移变得容易。
class RemoveStateFromProduct < ActiveRecord::Migration
def self.up
backup_data = []
Product.all.each do |product|
backup_data << {:id => product.id, :state => product.state}
end
backup backup_data
remove_column :products, :state
end
def self.down
add_column :products, :state, :string
restore Product
end
end
IIRC,在迁移中更改数据类型时,您将拥有 IrreversibleMigration。
我认为可以的另一种情况是当您进行合并迁移时。在这种情况下,“向下”实际上没有意义,因为它会删除所有表(合并后添加的表除外)。这可能不是你想要的。