En RoR resulta muy cómodo utilizar la herramienta rake para manejar la estructura de una BD así como el cambio de versiones. Recientemente me topé con el caso de necesitar agregar una llave externa FK a una tabla existente, en intente hacer la referencia normalmente según rake
ruby script/generate migration add_comment_to_post post:reference
Pero esto no resultó, luego de horas de búsqueda y lectura me doy cuenta que no es posible agregar una FK a una tabla existente utilizando el generador migrate pero que si se puede hacer manualmente y reutilizarse par futuro.
Creemos la migración,
ruby script/generate migration add_post_to_commet
luego vamos a editar db/migrate/XXXX_add_post_to_comment.rb como nos sugiere el nombre, vamos a pasar el id de la tabla post a la tabla comment.
#add_user_to_post.rb
require "migration_helpers"
class AddUserToPost < ActiveRecord::Migration
extend MigrationHelpers
def self.up
#add_foreign_key (:mainentity, :mainentity_id, secundaryentity)
add_foreign_key(:users, :id, :posts)
end
def self.down
#add_foreign_key (:mainentity, :mainentity_id, secundaryentity)
remove_foreign_key(:users, :id, :posts)
end
end
Para comprender lo que acabamos de hacer hay que crear el siguiente helper helpers/migration_helpers.rb
#migration_helpers.rb
module MigrationHelpers
def add_foreign_key(from_table, from_column, to_table, options = {})
constraint_name = foreignkey_name(from_table, to_table)
column_name = foreignkey_column_name(from_table, from_column)
execute %{alter table #{to_table} add #{column_name} INT not null} if options[:add_column]
execute %{alter table #{to_table}
add constraint #{constraint_name} foreign key
#{index_name(column_name)} (#{column_name}) references #{from_table}(#{from_column})
#{ options[:cascade].map{ |cas| " ON #{cas} CASCADE" }.join if options[:cascade] }
}
end
def remove_foreign_key(from_table, from_column, to_table, options = {})
constraint_name = foreignkey_name(from_table, to_table)
column_name = foreignkey_column_name(from_table, from_column)
execute %{alter table #{to_table} drop foreign key #{constraint_name}}
execute %{alter table #{to_table} drop #{column_name}} if options[:remove_column]
end
def create_index(table, *columns)
execute %{create index #{index_name(columns)} on #{table} (#{columns.join(',')}) }
end
def remove_index(table, *columns)
execute %{drop index #{index_name(columns)}on #{table}}
end
private
def foreignkey_column_name(from_table, from_column)
"#{from_table.to_s.singularize}_#{from_column}"
end
def foreignkey_name(from_table, to_table)
"fk_#{from_table}_to_#{to_table}"
end
def index_name(*columns)
"idx_#{columns.join('_')}"
end
end
Con esto estamos listos para hacer
rake db:migrate
y se crea una nueva columna llamada post_id en la tabla comments utilizando rake migrations al igual que se destruye cuando se cambia de version.
La historia completa: http://railsforum.com/viewtopic.php?pid=85698