RoR: Agregar FK a una tabla

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

Tagged ,

One thought on “RoR: Agregar FK a una tabla

  1. Igor says:

    post = models.ForeignKey(Post)

    😉

Leave a Reply

Your email address will not be published. Required fields are marked *