如何知道抽取任务已经 运行 在 rails?

How to know that a rake task has been run in rails?

我的 Rails 应用程序中有一个迁移,我想 运行 仅当特定的 rake 任务已经 运行 时,否则我会失去一个一堆数据。以下是我想做的事情:

if has_rake_task_been_run?
  remove_column :transactions, :paid_by
end

目前,反正我是找不到了,只能手动保证这个东西了。有什么解决办法吗?

使用rake task进行数据迁移是一个非常冒险的想法。不这样做的几个原因:

  1. 即使您设法确定您的 rake 任务是否已完成,您的迁移仍将被标记为已完成并且您将无法重播它。唯一的解决办法是在您的迁移中引发异常。

    不,您也无法回滚该迁移。如果 rake 任务在迁移 运行 后完成,回滚将尝试添加已经存在的列。

  2. 新开发人员从头开始设置数据库会变得非常痛苦,因为他们需要知道 运行 什么时候执行哪些 rake 任务。更不用说 rake db:migrate 执行所有迁移。

  3. 您正在用不可重复使用的任务污染您的 rake 任务列表

看起来你所做的只是一个常规的数据迁移,所以你的 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,它就无法正常工作(我犯过一次这个错误,花了一段时间才找出问题所在)。解决方法是在迁移 class 之外定义它,但是这些 classes 在所有迁移中都可用,并且可能会受到您的实际模型代码的影响(尤其是在预加载所有模型的生产中).简而言之,我目前仍在研究使用 STI 进行数据迁移。

万一有人来这里,我们成功地使用了after_partyrails库。库通过简单的机制维护已经执行过的rake任务,迁移任务变得容易。