Migrations in Rails allow you to incrementally change your database schema using pure Ruby by generating auto-numbered class files that can be checked into your version control system and easily deployed with a rake task. Perhaps the best part about this approach is that it’s database agnostic: it doesn’t matter whether you’re using mysql, or postgresql, or whatever—as long as the adapter supports migrations, you don’t need to worry about writing database specific sql statements anymore.
Prior to migrations, I would usually create sql files containing any necessary transformations and keep them under version control. This approach works for a while, and if you’re the only one working on the project you may not notice an immediate need for migrations because your schema is easy to manage. But trust me, as soon as you begin working with others, or if you have to deploy database changes in the production environment, you’ll be glad you took the time to learn about migrations.
Enough talk. Let’s dig in by creating our first migration. From your Rails root, type
./script/generate migration Genesis
This will create a new file in db/migrate called 001_genesis.rb. (Note that the numeric prefix is automatic; since this is our first migration, it is numbered as such). This file has a single class that extends ActiveRecord::Migration containing two empty methods: up and down. For each migration, you define instructions for updating in the up method, and use the down method to roll back any changes. So, if you were to, say, create a new table in the up method, you could drop the table in the down method thereby reversing your changes. Rails provides a number of methods that you use to create and drop tables, add and remove columns, rename columns, and more (see the AR::ConnectionAdapters::SchemaStatements documentation for the complete list of them).
Let’s get to work on on the up method by creating a new table. We’ll let the code speak for itself:
class Genesis < ActiveRecord::Migration
def self.up
create_table :messages do |t|
t.column :name, :string
t.column :body, :text
end
end
...
end
As you can see, `create_table` takes a table name and a block. Inside the block, we pass a fieldname and a format type to the column method. That’s all there is to it! If you were to run `rake migrate` right now, this simple migration would create a new table called messages with the columns id, name, and body. You might have noticed that we didn’t even bother creating the ubiquitous auto_increment id column. That’s because we don’t need to. If it’s omitted, Rails is smart enough to go ahead and add it for us, saving us the typing.
Now, creating tables is all right and good if you’re starting from scratch. But if this is an existing project I bet most of us will have already created a db/create.sql file that houses a ddl full of tables and maybe even data to be initialized. Ideally, we’d like our first migration to set all this up for us, but that might be a lot of work depending on the size our our schema. Wouldn’t it be nice to easily generate a our first set of transformations from our existing schema without having to type them all in by hand?
Rake to the rescue. Rails includes a rake task called `db_schema_dump` that will generate a programatic representation of your existing database structure in just the flavor we need for a migration. Go ahead and give it a try:
$ rake db_schema_dump
After rake is all finished, you should find a fresh new file in your db/ directory called schema.rb. Open it up and have a look. See? There are all the calls to create_table already done for you. All you have to do is copy everything inside the block given to “ActiveRecord::Schema.define” into the up method of your migration and you’re ready to rock.
With your initial schema defined in the up method, it’s time to give it a full test. But before we do that, we should probably put something in the down method. Given that this is our first migration we can’t really roll back to anything (other than an empty database, I guess), so you might call this migration irreversible. Well, wouldn’t you know it—there’s a special exception you can raise for rare situations such as this called (you guessed it) IrreversibleMigration. Our down method, then, would look like this:
def self.down
raise IrreversibleMigration
end
Let’s try out our migration. Drop and recreate your development database so that we have an empty canvas to start with, and then run $ rake migrate. After a few seconds of crunching, rake will unceremoniously complete its task and leave you with a blinking cursor at the command prompt. But if all went according to plan, your database should be chalk full of tables again. Go check it out.
Sometimes our schema definitions initialize data for us. You’ll be happy to know that adding data with migrations is easy and practical. Here’s a quick example:
def self.up
create_table :messages do |t|
t.column :name, :string
t.column :body, :text
end
Message.create :name => 'Test',
:body => 'Drank my sleep from a can'
end
Notice that we’re using the Message model to create our data (Message.create). So, the Model’s rules (like validations, callbacks, etc.) are always enforced. Since Rails is of the opinion that all access to the database should be through the Model, this is a good thing. You wouldn’t want to execute a sql file that added invalid data to your database, would you? Of course not.
Let’s take a typical example of a User model that uses the before_create callback to hash the password. If I were to add a new user via sql, how would I go about hashing their password? (No, putting the hashed string directly in the sql isn’t a good idea—what if you change the hash’s salt?) Initializing data using the Model means that the callbacks will fire, and the password will get hashed as expected. Nice, huh?


Migrations are great, but remember: The filename and class name are related. If you make a migration by hand called 007_chunky_bacon.rb, the class must be called ChunkyBacon. Anything else will give you an “uninitialized constant” error.
... rake migrate track006, track007, again and again …
Great article! It really demystified migrations for me. However, while I can do a ‘rake migrate’ to create my database tables and columns, when I include the Message.create statement ‘rake migrate’ fails:
rake aborted! uninitialized constant Message
From what I’ve been able to find, this is usually do to a missing require statement or a type (bad case). Neither of these seem to be the problem though. I’m very new to ruby and ruby on rails, so be nice if it’s obvious! :)
Vlad – This is probably because you don’t have a
Messagemodel. Migrations allow you access to all your model classes, but they need to exist—in this case,Messageis expected to contain two fields,nameandbody.Despite my example, practise has taught me that using your models inside migrations makes them rather brittle. If you one day decide to delete or rename the Message model, any migrations that use it will fail.