Search code examples
ruby-on-railsmigrationassociationsrails-activerecordrails-migrations

Rails array transfer to new join table


I have a table Exercise and I'm transferring arrays from a column, dynamic, to a new join table ExerciseDynamicMuscle. This way each :id in the array becomes it's own object/record. I've tested it and it works or at least when checking the records it has transferred correctly, I'm just curious if there is anything else I should add or look out for?

Migration info for table being transferred to:

create_table :exercise_dynamic_muscles do |t|
  t.belongs_to :exercise
  t.belongs_to :muscle

  t.timestamps
end

Migration created to do the transfer:

Exercise.find_each do |e|
  e.dynamic.each do |mu|
    ExerciseDynamicMuscle.create(
      muscle_id: mu, 
      exercise_id: e.id  
    )
  end
end

The ExerciseDynamicMuscle schema:

  create_table "exercise_dynamic_muscles", force: :cascade do |t|
    t.bigint "exercise_id"
    t.bigint "muscle_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["exercise_id"], name: "index_exercise_dynamic_muscles_on_exercise_id"
    t.index ["muscle_id"], name: "index_exercise_dynamic_muscles_on_muscle_id"
  end

Once the data is transferred I plan to drop the columns via another migration:

remove_column :exercises, :dynamic 

So there will be two separate migrations:

  • One to transfer the data
  • One to drop the columns

Solution

  • Usually migrations are reversible. To do that, you need something like

    def up
      create_table :exercise_dynamic_muscles do |t|
        t.belongs_to :exercise
        t.belongs_to :muscle
    
        t.timestamps
      end
    
      Exercise.find_each do |e|
        e.dynamic.each do |m_id|
          ExerciseDynamicMuscle.create!(
            muscle_id: m_id,
            exercise_id: e.id
          )
        end
      end
    
      remove_column :exercises, :dynamic
    end
    
    def down
      change_table :exercises do |t|
        t.text :dynamic, array: true, default: []
      end
    
      ExerciseDynamicMuscle.find_each do |edm|
        exercise = edm.exercise
        exercise.dynamic << edm.muscle_id
        exercise.save!
      end
    
      drop_table :exercise_dynamic_muscles
    end
    

    Firstly you create new table, than migrate data to this table from array columns, and finally drop array column

    Rollback migration is everything the same but in reverse order only