Reputation: 55
I have an events table and sessions table. Events has_many sessions, this is the association. Now I want to move the time_zone column from the sessions table to events table only. So how do I do this with help of migrations. How do I move the existing records for time_zone in sessions table to events table?
Upvotes: 1
Views: 7357
Reputation: 2934
First, you need to be sure that sessions associated with the same event have the same time zone. You can do this with:
Session.group(:event_id).count(:time_zone)
This will return a hash mapping an event_id
to the number of time zones associated with it. This number should always be one.
Second, I recommend that you first add events.time_zone
and start using it and remove sessions.time_zone
in a separate migration after the new code has been in production for some time and is proved to work.
Third, the migration to add events.time_zone
should look like this (I added some comments for clarity):
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
Note that I redefined models in the migration. It's crucial to do so because:
Once you're sure your changes work as expected you can remove sessions.time_zone
. If something goes awry you can simply roll back the above migration and restore a working version easily.
Upvotes: 4
Reputation: 19
You can simply use the following migration.
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
Upvotes: 0