Migrations, data loads and changes to the model


#1

Guys,

I’ve run across an interesting scenario with my migrations, and I hope
you might share some insight.

Let’s say I have 3 migrations and two Models (Person and Car):
001_initial_schema.rb
002_data_load.rb
003_add_car_association.rb

The first creates the initial schema…the second loads default data
using model objects of Person to do so, and the third creates a new
table, “cars”, and modifies the “persons” table to create a :has_one
relationship of Person[1]->[1]Car.

Let’s also say I want all Persons to have at least one car, so I change
my Person model to have:

def after_initialize
self.car = Car.new unless self.car
end

Now, what happens, is that when I issue a “rake migrate” on my intial
database
When I start from scratch (new database) and do a rake migrate, my data
load fails because it uses the model class Person, yet Person’s code now
has an after_initialize that expects the Car association to be present.
Because Car isn’t created until migration 003, I’m essentially up the
creek. rake throwns an InvalidStatement exception.

Has anyone found a decent way of handling these scenarios?

Thanks!
John


#2

Thanks!
John

I prefer to keep my migrations free of my models for this very reason.
I’ll create temp models if I need to do basic queries, however.

class MyMigration < Migration
class Article < AR::Base
end

def self.up
end

def self.down
end
end

This essentially makes the model’s name MyMigration::Article, and
won’t clash with your app’s Article model or any other migrations’
Article models.

disclaimer: I haven’t actually tried this. However, I ran into this
issue on my Mephisto blogging tool and created temp models like
TempArticle. Someone had a question on the Rails-Core ML and led me
to this idea. I’ll try it next.

http://techno-weenie.net/svn/projects/mephisto/trunk/db/migrate/


Rick O.
http://techno-weenie.net


#3

I’ve run across an interesting scenario with my migrations, and I hope
you might share some insight.

Has anyone found a decent way of handling these scenarios?

http://scottstuff.net/blog/articles/2005/10/31/migrating-in-two-dimensions
http://rails.techno-weenie.net/tip/2006/2/23/safely_using_models_in_migrations


#4

Rick O. wrote:

def self.down
end
end

This essentially makes the model’s name MyMigration::Article, and
won’t clash with your app’s Article model or any other migrations’
Article models.

disclaimer: I haven’t actually tried this.
Having googled a bit, and seen your conversation on the rails-core list,
I did try it. It works. Very nicely :slight_smile:

It’s a situation that I’m surprised doesn’t come up more often. Changes
to models breaking old migrations must be quite a common occurrence,
both because of lifecycle callbacks and because of relationship changes.
The rules of thumb I’ve come up with are:

  • Always use local models in migrations (as above). An alternative
    would be a patch to AR that exposed create_without_callbacks, but that
    was more complex than I wanted to get in to, and wouldn’t cover
    everything.
  • Never rely on dynamic associations, always use foo[:bar_id] = qux[:id]
    rather than foo.bar = qux.

I’ve found that following those rules allows changes to model code while
also letting your app bootstrap itself in a single rake migrate pass.