Migrations

Creating new tables

Migrations are one more rails feature that makes developing less painful. With them you don't need to write SQL to set up tables in databases for every environment. Lets see how.

Suppose, we create a new model Book with attributes title, published year, and isbn:

ruby  script/generate model book title:string, published_year:integer, isbn:string
      exists  app/models/
      exists  test/unit/
      exists  test/fixtures/
      create  app/models/book.rb
      create  test/unit/book_test.rb
      create  test/fixtures/books.yml
      create  db/migrate
      create  db/migrate/20090801123115_create_books.rb
The last line shows us that rails created migration file for our new model. Migration files live under db/migrate.
Lets study what is inside this file:
class CreateBooks < ActiveRecord::Migration
  def self.up
    create_table :books do |t|
      t.string, :title
      t.integer, :published_year
      t.string, :isbn

      t.timestamps
    end
  end
  def self.down
    drop_table :books
  end
end

We see here that the class CreateBooks inherits from ActiveRecord::Migration.

Then two methods defined: self.up and self.down (actually, up and down). Up is called when this migration is added to the database. Down is called when a migration is undone (or reversed).

The Up method starts with create_table method, which tells rails to create new table with name 'books'. As you may recall, the table name should be pluralized version of model name. Then in a block it passes attributes of table: columns and their types. In old resources you may find another way of attribute definition:
create_table :books do |t|
      t.column :title, :string
      t.column :published_year, :integer
      t.column :isbn, :string

This was the format for migrations before rails 2.0. Although, this format will still work.

t.timestamps is a magic method that creates two columns: updated_at and created_at.

The Down method only contains one line with method drop_table, that tells rails to drop table with name 'books'.

For relations like HABTM you don't need column id for table. Just tell rails not to create this column:
create_table :books_authors, :id => false do |t| 

Altering tables with migrations

In the development process sometimes we need to add columns to table or add table for HABTM relationship. So we don't need to generate a model, just the database migration.
Rails provides special generator for this: script/generate migration.

Lets add column description to model Book.
ruby  script/generate migration AddDescriptionToBook description:text
      exists  db/migrate
      create  db/migrate/20090801140441_add_description_to_book.rb

Notice that the name passes the script/generate migration AddDescriptionToBook gives the generator some clues as to what you will be doing in that migration so it will automatically generate the up and down methods with what it believes you are trying to do in that migration.

If you look at the a generated migration file 20090801140441_add_description_to_book.rb:
class AddDescriptionToBook < ActiveRecord::Migration
  def self.up
    add_column :books, :description, :text
  end
  def self.down
    remove_column :books, :description
  end
end

You will see that the Up method adds column (with method add_column) to table books, that title for the column is description and its type is text.
The Down method removes column description from table books.

Since rails 2.1 we have another way to write this type of migration:
class AddDescriptionToBook < ActiveRecord::Migration
  def self.up
    change_table :books do |t|
      t.text :description
    end
  end
  def self.down
    change_table :books do |t|
      t.remove_column :description
    end
  end

Adding index with migration

One of the ways to improve performance of your app is through optimization of database with indexes.
ActiveRecord::Migration provides method add_index to add an index to the database and method remove_index to remove it.

Lets add index to CreateBooks migration (and don't forget to remove it):
class CreateBooks < ActiveRecord::Migration
  def self.up
    create_table :books do |t|
      t.string, :title
      t.integer, :published_year
      t.string, :isbn

      t.timestamps
    end
      add_index :books, :title
  end

  def self.down
    drop_table :books
    remove_index :books, :title
  end

end

Rake tasks to work with migrations

To see all available rake tasks for database type

rake -T db

If you have filled file config/database.yml with credentials to work with your database,then you can tell rails to perform actions with it.

To create the database:

rake db:create
to create database for current environment (default = development). or
rake db:create:all
to create databases for all environments. To drop the database for the current environment enter:
rake db:drop
or for all environments:
 rake db:drop:all

To perform migrations from db/migrate :

rake db:migrate
.
If you want to rollback to specified version of migration, type:
rake db:migrate VERSION=20090801140441

Additional resources
  1. Railsguides: Migrations
  2. Railscasts: episode 107. Migrations in rails 2.1

Also available in: HTML TXT