Created
August 7, 2015 22:46
-
-
Save jakecraige/f1a1bffef714be2b61fb to your computer and use it in GitHub Desktop.
Example migration migrating from integer IDs as a primary key to uuid IDs while maintaining existing associations.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class ChangeIdIntToUuid < ActiveRecord::Migration | |
def up | |
remove_foreign_key "business_debts", "loan_applications" | |
remove_foreign_key "guarantors", "loan_applications" | |
remove_foreign_key "guarantors", "users" | |
table_names = [ | |
:loan_applications, | |
:business_debts, | |
:case_owners, | |
:guarantors, | |
:document_collections, | |
:users, | |
:legacy_users, | |
] | |
update_primary_keys(table_names) | |
update_has_many_associations(table_names) | |
remove_temporary_columns(table_names) | |
add_foreign_key "business_debts", "loan_applications" | |
add_foreign_key "guarantors", "loan_applications" | |
add_foreign_key "guarantors", "users" | |
add_index :guarantors, :loan_application_id | |
add_index :guarantors, :user_id | |
add_index :business_debts, :loan_application_id | |
add_index( | |
:document_collections, | |
[:documentable_type, :documentable_id, :name, :year], | |
unique: true, | |
name: "index_document_collections_by_type", | |
) | |
end | |
def down | |
raise ActiveRecord::IrreversibleMigration.new( | |
"This cannot be undone because it's destructive of the original IDs." | |
) | |
end | |
def update_primary_keys(table_names) | |
table_names.each do |table| | |
update_primary_key_to_uuid(table) | |
end | |
end | |
def update_primary_key_to_uuid(table_name) | |
add_column table_name, :temp_uuid, :uuid, default: "uuid_generate_v4()" | |
execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{table_name}_pkey;" | |
rename_column table_name, :id, "#{original_prefix}id" | |
rename_column table_name, :temp_uuid, :id | |
execute "ALTER TABLE #{table_name} ADD PRIMARY KEY (id);" | |
end | |
def update_has_many_associations(table_names) | |
has_many_associations = table_names.map do |table_name| | |
model_class = constantize_table(table_name) | |
model_class.reflect_on_all_associations(:has_many) | |
end.flatten | |
has_many_associations = has_many_associations.select do |association| | |
association.instance_of?(ActiveRecord::Reflection::HasManyReflection) | |
end | |
has_many_associations = has_many_associations.uniq do |association| | |
[association.class_name, association.foreign_key] | |
end | |
has_many_associations.each do |association| | |
update_has_many_association(association) | |
end | |
end | |
def update_has_many_association(association) | |
parent_class = association.active_record | |
model_class = association.class_name.constantize | |
table_name = model_class.table_name | |
foreign_key = association.foreign_key | |
original_foreign_key = "#{original_prefix}#{foreign_key}" | |
rename_column table_name, foreign_key, original_foreign_key | |
add_column table_name, foreign_key, :uuid | |
parent_class.reset_column_information | |
model_class.reset_column_information | |
update_all_model_associations( | |
model_class: model_class, | |
parent_class: parent_class, | |
original_foreign_key: original_foreign_key, | |
foreign_key: foreign_key | |
) | |
end | |
def update_all_model_associations( | |
model_class:, | |
parent_class:, | |
original_foreign_key:, | |
foreign_key: | |
) | |
model_class.find_each do |instance| | |
association_id = instance.send(original_foreign_key) | |
parent_model = parent_class.find_by( | |
"#{original_prefix}id": association_id | |
) | |
if parent_model | |
instance.update_column(foreign_key, parent_model.id) | |
end | |
end | |
end | |
def remove_temporary_columns(table_names) | |
table_names.each do |table_name| | |
model_class = constantize_table(table_name) | |
columns_to_remove = model_class.column_names.select do |column| | |
column.start_with?(original_prefix) | |
end | |
remove_columns table_name, *columns_to_remove | |
end | |
end | |
def constantize_table(table_name) | |
table_name.to_s.singularize.camelize.constantize | |
end | |
def original_prefix | |
"original_" | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment