views:

132

answers:

2

Let's say you have "lineitems" and you used to define a "make" off of a line_item.

Eventually you realize that a make should probably be on its own model, so you create a Make model.

You then want to remove the make column off of the line_items table but for every line_item with a make you want to find_or_create_by(line_item.make).

How would I effectively do this in a rails migration? I'm pretty sure I can just run some simple find_or_create_by for each line_item but I'm worried about fallback support so I was just posting this here for any tips/advice/right direction.

Thanks!

A: 

You can put regular ruby code in your migration, so you can create the new table, run some code across the old model moving the data into the new model, and then delete the columns from the original model. This is even reversible so your migration will still work in both directions.

So for your situation, create the Make table and add a make_id to the lineitem. Then for each line item, find_or_create with the make column on lineitem, setting the returned id to the new make_id on lineitem. When you are done remove the old make column from the lineitem table.

danivovich
+1  A: 

I guess you should check that the Make.count is equal to the total unique makes in lineitems before removing the column, and raise an error if it does not. As migrations are transactional, if it blows up, the schema isn't changed and the migration isn't marked as executed. Therefore, you could do something like this:

class CreateMakesAndMigrateFromLineItems < ActiveRecord::Migration
  def self.up
    create_table :makes do |t|
      t.string :name
      …
      t.timestamps
    end

    makes = LineItem.all.collect(:&make).uniq
    makes.each { |make| Make.find_or_create_by_name make }
    Make.count == makes.length ? remove_column(:line_items, :make) : raise "Boom!"

  end

  def self.down
    # You'll want to put logic here to take you back to how things were before. Just in case!
    drop_table :makes
    add_column :line_items, :make
  end
end
Steve Graham