The blog of , a Ruby on Rails development team

How to use models in your migrations (without killing kittens)

Sometimes a migration needs to do more than add or remove a column. When you must convert existing data, migrations can get nasty because SQL is all you can use. You can never call models in a migration because your models will evolve away from your migrations and everyone will die. Seriously, don't do that. Think of the children!

Fortunately, there is a way to use ActiveRecord magic in your migrations and still get the girl. By inlining classes you decouple your migration from any future changes in your models. Here is an example:

Say you have two classes Article and Vendor. Each Article has_many :vendors. Now you want to introduce a boolean flag current to your Vendor class, which determines whether the article is currently being procured from that vendor.

But what about the 14000 articles already in the database? You call up your client and decide on the following migration rule: The first vendor created for an article is to be flagged as "current".

While you can express that rule in pure SQL, you'd much rather use vanilla Ruby. So you write your migration like this:

class AddCurrentToVendor < ActiveRecord::Migration

  class Vendor < ActiveRecord::Base
  end

  class Article < ActiveRecord::Base
    has_many :vendors, :class_name => 'AddCurrentToVendor::Vendor', :order => 'created_at'
  end

  def self.up
    add_column :vendors, :current, :boolean
    Article.all.each do |article|
      article.vendors.first.andand.update_attribute(:current, true)
    end
  end

  def self.down
    remove_column :vendors, :current
  end
end

Notice how our two classes were namespaced into the migration class, so Vendor becomes AddCurrentToVendor::Vendor.

Growing Rails Applications in Practice
Check out our new e-book:
Learn to structure large Ruby on Rails codebases with the tools you already know and love.

Recent posts

Our address:
makandra GmbH
Werner-von-Siemens-Str. 6
86159 Augsburg
Germany
Contact us:
+49 821 58866 180
info@makandra.de
Commercial register court:
Augsburg Municipal Court
Register number:
HRB 24202
Sales tax identification number:
DE243555898
Chief executive officers:
Henning Koch
Thomas Eisenbarth