Rants About Rails Database Migrations

Migrations as version control for databases

When I first learned about Rails migration, I immediately thought that dropping down a revision would be something you would do often. In actuality, Rails migrations is more like a version control for databases. Since databases changes require modifications to both code and data, you cannot simply use a source code version control system like Subversion or Sourcesafe. You need a more advanced revisioning system for databases… hence Rails Migrations.

Because of my inexperience with databases, I mistakenly thought that the purpose of migrations was to allow the ability to go up and down migrations at will.. But the purpose is more to restore an older version of the database like you would restore an older version of source code with Subversion. I would think that when you migrate down, all of your business logic would probably migrate down at the same time. What are thoughts on this? Any comments?

In a nutshell, Rails migrations are really just a database agnostic way of running SQL scripts in an organized, standardized way. Migrations can obviously be used outside of web development.

Relationship between model and migration

In Rails, a model internally maps itself to a database table. The table in the database must be the plural form of the model’s class. If we generate a model called User, Rails automatically looks for a table called users in the database.

You can use the Rails generator to generate both the model and a corresponding migration as shown here (You can also generate a migration by itself – for example, add_column to an existing model)

ruby script/generate model User #run Rails model generator

The model and migration are both generated:

app/models/user.rb #model

db/migrate/001_create_users.rb #migration that creates database table

And the generator also generates some test stuff:

test/unit/user_test.rb #unit test for the model

test/fixtures/users.yml #sample data for the model

The migration is generated so that it will use the plural form of the model for the database table name. The important thing to note is that after the migration is generated, the class name of the model (User) and the class name for the migration (CreateUsers) are not linked in anyway. The only link is at runtime and that is only between the model and the database table name. For example, when you do a query with the User model (User.find), then ActiveRecord will automatically query on the users table in the MySQL database (or whichever database you’re using). The migration is only used to set up the database and this is never done during runtime.

Model (app/models/user.rb)

  class User < ActiveRecord::Base

Migration (db/migrate/001_create_users.rb)

  class CreateUsers < ActiveRecord::Migration
    def self.up
      create_table :users do |t|
        t.column :user_id, :int
      end
    end

    def self.down
      drop_table :users
    end
  end

The cool part about ActiveRecord’s ORM is that the model never needs to maintain the column names. It learns them during runtime, as seen by this empty User class.

Migrations on a per table basis

One drawback of Rails migrations is that all migrations occur at the database level, not the table level. If you want to restore the database for only one table, you’ll have to do something special, which I haven’t figured out just yet.

Renaming the model when the model is referenced inside the migration

If you rename a model and the model is referenced inside a migration, it might break your migrations. When would you ever reference the model inside the migration? When you want to make changes to the actual data in the database, it’s easier to code with the model than raw SQL. In this case, you can actually define the model in the migration itself to prevent it from breaking if you rename the model. This is described in Rails Recipes by Chad Fowler

Advanced features and options of Rails migrations:

  1. Mike Perham’s writeup of the usage :limit, :null, add_index, and foreign key support
  2. Some advanced migration features (:force, :temporary, :opitons, execute native SQL)

Basics of Rails migrations:

  1. Basics of Rails migrations
  2. Ruby on Rails Migrations Explained
  3. Damon ClinkScales’ very illustrative presentation on Rails Migrations
Share