使用rails迁移中的数据更新列

时间:2017-01-10 09:15:31

标签: ruby-on-rails-4 rails-migrations

我有一个事件表和会话表。事件has_many会话,这是关联。现在我想将time_zone列从sessions表移到事件表。那么我如何在迁移的帮助下做到这一点。如何将sessions表中time_zone的现有记录移动到事件表?

2 个答案:

答案 0 :(得分:3)

首先,您需要确保与同一事件关联的会话具有相同的时区。你可以这样做:

Session.group(:event_id).count(:time_zone)

这将返回一个哈希映射event_id到与之关联的时区数。这个数字应该总是一个。

其次,我建议您首先添加events.time_zone并开始使用它,并在新代码投入生产一段时间后在单独的迁移中删除sessions.time_zone并证明可以正常工作。

第三,添加events.time_zone的迁移应该如下所示(为了清晰起见,我添加了一些注释):

class AddTimeZoneToEvents < ActiveRecord::Migration
  class Event < ActiveRecord::Base; end
  class Session < ActiveRecord::Base; end

  def up
    # Add a NULLable time_zone column to events. Even if the column should be
    # non-NULLable, we first allow NULLs and will set the appropriate values
    # in the next step.
    add_column :events, :time_zone, :string

    # Ensure the new column is visible.
    Event.reset_column_information

    # Iterate over events in batches. Use #update_columns to set the newly
    # added time_zone without modifying updated_at. If you want to update
    # updated_at you have at least two options:
    #
    #   1. Set it to the time at which the migration is run. In this case, just
    #      replace #update_columns with #update!
    #   2. Set it to the maximum of `events.updated_at` and
    #      `sessions.updated_at`.
    #
    # Also, if your database is huge you may consider a different query to
    # perform the update (it also depends on your database).
    Event.find_each do |event|
      session = Session.where(event_id: event.id).last
      event.update_columns(time_zone: session.time_zone)
    end

    # If events don't always need to have time zone information then
    # you can remove the line below.
    change_column_null :events, :time_zone, false
  end

  def down
    remove_column :events, :time_zone
  end
end

请注意,我在迁移中重新定义了模型。这样做至关重要,因为:

  1. 原始模型可能有回调和验证(当然,您可以跳过它们,但它是一个额外的预防措施,可以提供零价值)。
  2. 如果您在未来6个月内移除模型,迁移将停止工作。
  3. 确定您的更改按预期工作后,您可以删除sessions.time_zone。如果出现问题,您可以简单地回滚上述迁移并轻松恢复工作版本。

答案 1 :(得分:0)

您只需使用以下迁移。

class Test < ActiveRecord::Migration
  def change
    add_column :events, :time_zone, :string

    Event.all.each do |e|
      e.update_attributes(time_zone: e.sessions.last.time_zone)
    end

    remove_column :sessions, :time_zone
  end
end