我在我的Rails应用程序中进行了迁移,我只想在运行特定的rake任务时运行,否则我将丢失一堆数据。以下是我想做的事情:
if has_rake_task_been_run?
remove_column :transactions, :paid_by
end
目前,我无法找到,而不是手动确认此事。有什么工作吗?
答案 0 :(得分:3)
使用rake任务进行数据迁移是一个非常危险的想法。有几个理由不这样做:
即使您设法确定您的佣金任务是否已完成,您的迁移仍会标记为已完成,但您无法重播。唯一的方法是在迁移过程中引发异常。
不,你也无法回滚那次迁移。如果rake任务在迁移运行后完成,则回滚将尝试添加现有列。
新开发人员从头开始设置数据库会变得很痛苦,因为他们需要知道哪些rake任务要运行。没有提到rake db:migrate
执行所有迁移。
您使用不可重复使用的任务污染您的佣金任务列表
您所做的似乎只是常规的数据迁移,因此您的rake任务完成的所有工作实际上都应该是您迁移的一部分。这甚至可以让你进行可逆的数据迁移(在大多数情况下)。
但请注意,数据迁移并不像常规的仅计划迁移那么简单。因为您的迁移应该完全独立于您的代码(因为它们将来会工作,即使迁移的模型已从您的代码库中完全删除),因此通常的做法是重新定义您将要在迁移中使用的模型(仅迁移所需的位数)。不幸的是,这听起来并不那么简单,老实说,我仍然在寻找一个完美的解决方案。到目前为止我见过的最好的事情很简单(我假设paid_by
曾经是字符串而你更改了paid_by_id
,它引用了用户):
class YOURMIGRATIONNAME < ActiveRecord::Migration
class Transaction < ActiveRecord::Base
belongs_to :paid_by, class_name: "User"
end
class User < ActiveRecord::Base
end
def up
add_column :transaction, :paid_by_id, :integer
Transaction.transaction do # for speed
Transaction.find_each do |t|
t.paid_by_id = User.find_by(username: t[:paid_by])
t.save! # Always banged save in migration!
end
end
remove_column :paid_by
end
def down
add_column :transaction, :paid_by, :string
Transactions.transaction do
Transaction.find_each do |t|
t[:paid_by] = t.paid_by && t.paid_by.username
t.save!
end
end
remove_column :transactions, :paid_by_id
end
使用上述代码的唯一缺点是,如果这些模型中的任何一个使用STI,它将无法正常工作(我曾经犯过一次错误,需要一段时间才能找到什么&#39;错了)。解决方法是在迁移类之外定义它,但是这些类在所有迁移中都可用,并且可能会受到实际模型代码的影响(特别是在预加载所有模型时的生产中)。简而言之,我目前仍在研究使用STI进行数据迁移。
答案 1 :(得分:2)
万一有人来了,我们成功使用了after_party rails库。通过简单的机制,该库可以维护已执行的rake任务,并且可以轻松执行迁移任务。