迁移数据——不仅是模式,Rails

14
有时候需要进行数据迁移。随着时间的推移,代码会发生变化,使用您的领域模型进行迁移将不再有效,从而导致迁移失败。迁移数据的最佳实践是什么?
我尝试举个例子来澄清问题:
考虑这个问题。您有一个迁移。
class ChangeFromPartnerAppliedToAppliedAt < ActiveRecord::Migration
  def up
    User.all.each do |user|
      user.applied_at = user.partner_application_at
      user.save
   end
 end

这个运行得非常好,当然。后来,您需要进行模式更改。

class AddAcceptanceConfirmedAt < ActiveRecord::Migration
  def change
    add_column :users, :acceptance_confirmed_at, :datetime
  end
end

class User < ActiveRecord::Base
  before_save :do_something_with_acceptance_confirmed_at
end

对于您来说,没有问题。它可以完美运行。但是,如果您的同事今天同时运行这两个,尚未运行第一个迁移,他将在运行第一个迁移时遇到此错误:

rake aborted!
An error has occurred, this and all later migrations canceled:
undefined method `acceptance_confirmed_at=' for #<User:0x007f85902346d8>

这不是团队合作的表现,他会修复你引入的 bug。你应该怎么做?

3个回答

14

这是一个完美的 在迁移中使用模型 的示例。

class ChangeFromPartnerAppliedToAppliedAt < ActiveRecord::Migration
  class User < ActiveRecord::Base
  end

  def up
    User.all.each do |user|
      user.applied_at = user.partner_application_at
      user.save
   end
 end

在 Mischa 的评论后进行了编辑

class ChangeFromPartnerAppliedToAppliedAt < ActiveRecord::Migration
  class User < ActiveRecord::Base
  end

  def up
    User.update_all('applied_at = partner_application_at')
  end
 end

为什么会有负分制度?在将答案标记为负面之前,有人能否解释一下为什么它不正确? - Salil
太棒了,Salil。只有无知者才会踩这个答案。我只是有点烦恼自己没有想到这个解决方案。太棒了! :) - oma
2
谁曾经给取消了,不用担心!显然这是正确的答案。只有一个备注@oma:我建议使用User.update_all('applied_at = partner_application_at')而不是User.all.each等。 - Mischa
如果按照Misha描述的方式操作,那么您不需要声明User类。但是,如果按照Misha所说的方法进行操作,则可以直接在SQL中执行此操作。或者,您可以使用ActiveRecord :: Base中的#reset_column_information方法,在不声明模型的情况下执行第一个示例。http://guides.rubyonrails.org/migrations.html#using-models-in-your-migrations - ChuckE
这种方法存在缺陷,因为类的定义重复了,似乎不正确。如果真正的类发生变化,例如它们的关联,这段代码将无法工作。多态关联还有一个问题——在此示例中,类是在ChangeFromPartnerAppliedToAppliedAt的范围内定义的。因此,与其他类的多态关联将无法工作。解决方案是编写迁移测试。我为这个问题创建了一个gem。看看这个 - https://github.com/ka8725/migration_data - ka8725
1
RailsGuides的链接已经失效,因为该部分已从文档中删除。如果你想阅读,这里是删除该部分的PR链接(就像我一样)https://github.com/rails/rails/pull/14208/files?short_path=0ffb86f#diff-0ffb86fcbcebd97b9d77338e08102907 - CamelBlues

13

最佳实践是:不要在迁移中使用模型。迁移会改变AR映射的方式,因此根本不要使用它们。全部使用SQL来完成。这样它将始终正常工作。

这个:

User.all.each do |user|
  user.applied_at = user.partner_application_at
  user.save
end

我会像这样做

update "UPDATE users SET applied_at=partner_application_at"

2
为什么最好不要在迁移中使用模型?有支持此观点的任何来源吗?Salil的答案似乎与您的陈述相矛盾。 - Mischa
2
这是最佳实践,因为对模式的内在更改会改变AR映射属性的方式。也就是说,您可能正在使用某些getter/setter/方法来实现模型,这些方法最终将被更新/删除。这意味着,如果您从头到尾运行迁移,它们将会中断。您始终依赖于具有稳定模式。当然,我的主要论点是:迁移在数据库上工作,因此它们应该使用那里理解的语言。即使是用于迁移的rails DSL(create_table等)最终也会被转换为原始SQL。这样可以实例化AR模型。 - ChuckE
在迁移中,这只是Rails/AR的又一个矛盾。 - ChuckE
2
好的,那是你的观点。我没有任何问题。但是,如果像你所声称的那样,这是最佳实践,那么一定有一些来源来支持它。最好是由Rails核心成员提供。 - Mischa
1
ChuckE,你是对的。在迁移中使用原始SQL是保持代码最新的最强大的解决方案。最近我发明了一个简单的解决方案,可能更容易。我在我的博客文章中描述了它。请查看 - http://railsguides.net/2014/01/30/change-data-in-migrations-like-a-boss/ - ka8725
显示剩余3条评论

0
有时候,“迁移数据”不能作为模式迁移的一部分执行,就像上面讨论的那样。有时,“迁移数据”意味着“修复历史数据不一致性”或“更新您的Solr/Elasticsearch索引”,因此这是一个复杂的任务。对于这些类型的任务,请查看这个宝石https://github.com/OffgridElectric/rails-data-migrations 这个宝石旨在将Rails模式迁移与数据迁移解耦,因此它不会在部署时导致停机,并使整体管理变得容易。

有趣。通常数据迁移是由于模式迁移而发生的。我同意,时不时我们也必须修复数据,尽管这更有可能与旨在防止更多相同坏数据的模式迁移有关。因此,我真的认为这不是正确的做法,任何数据迁移都直接与模式版本相关,以使其可运行。你有什么想法? - oma

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接