Using Models In Rails Migrations
It is quite tempting to use models in rails migrations.
At the time the migration is expected to run, the model class will have been updated already, so it is hard use that in the migration itself, even though it would be useful.
Consider this example:
class CreateUsers < ActiveRecord::Migration def self.up t.string :email, :null => false t.string :password, :null => false end # create a dummy user - with newer rails versions this should really go in seeds.rb! User.create!(:email => 'user@example.com', :password => 'demo') end def self.down drop_table :users end end
Doesn’t get simpler than this!
Now lets add another migration:
class AddSSNForUser < ActiveRecord::Migration def self.up add_column, :users, :ssn, :string, :null => false end def self.down remove_column, :users, :ssn end end
And a corresponding validation to the User model:
class User < ActiveRecord::Base # ... validates_presence_of :ssn # ... end
Now if you were to run the database migrations on an empty database, the first migration would fail because of the lack of a SSN. Worse, it is quite possible that the User class is now called something else.
How do you get around this issue:
class CreateUsers < ActiveRecord::Migration # define your own User class class User < ActiveRecord::Base #... end def self.up # clear all cache that rails maintains about the User class/table mapping User.reset_column_information create_table :users do |t| # whatever we did in the example above end # this statement will always work no matter what. User.create!(:email => 'user@example.com', :password => 'demo') end def self.down drop_table :users end end
Sometimes using SQL for performing data migrations could get quite cumbersome and unreadable, and using model objects is the simplest way to run any form of data migration. In such a case, it’s better to copy your model code into the migration.
I think that fundamental problem of your first migration is that it is not a migration.
You have migrated database and created an entry using the same script.
Migrations are there to modify database, not create data in it.
Greg
Greg Gigon
23 Nov 10 at 8:42 am
Two points:
1) If your model somehow depends on querying a 3rd party service to validate something (even though that is a BAd idea in and of itself, but, some domain require things like this), then using the model in your migration enforces that the other system is up and running during your deploy – BAD idea!
2) Totally related, but tangential, we encounntered situations where the data was inserted into the db using sql, and finally we are left with losing data integrity. To counter this, we wrote a rails plugin called active_record_validator. Send me an email offline if you are interested in talking abt this or looking at the code.
Vijay Aravamudhan
28 Nov 10 at 5:09 am