Last week we looked at creating our first Ruby on Rails Model.

Rails Models are backed by a database table. So for example, if you had an Article Model you would also need an articles database table.

In order to control our database schema and to make deployment and changes easier we can use migration files.

A migration file is a set of instructions that will be run against your database.

This means if you need to make a change to your database, you can capture that change within a migration file so it can be repeated by other installations of your project.

This will be extremely useful when it comes to deploying your application or working with other developers.

In today’s tutorial we will be taking a look at Rails migrations.

The purpose of Migrations

In order to fully understand migrations and why you need them, first it’s important to understand their purpose.

The majority of all web applications will need a database. A database is used for storing the data of the web application. So for example, that might be blog posts or your user’s details.

The database is made up of tables that store your data. Typically you would run SQL statements to create or modify the tables and columns of a database.

Rails introduces a specific Domain-specific language for writing instructions for how a database should be created. This saves you from writing SQL statements.

A migration is a file that contains a specific set of instructions for the database. For example, last week we created a migration file to create the articles table with columns for title and body.

When this migration file is run, Rails will be able to make the changes to the database and automatically create the table for us.

Over time as the database evolves, the migration files will act as a versioned history of how the database has changed. This means you will be able to recreate the database from the set of instruction files.

What are the benefits of using Migrations?

There are a number of benefits to using Migrations as part of your Rails project.

Firstly, your application is going to be pretty useless without a database. When someone grabs a copy of the code they need to set up a local version of the database. Instead of passing around a file of SQL statements, the Rails project will automatically have everything it needs to recreate the database.

Secondly, when working with other developers, you will likely face a situation where one developer needs to modify the database in order to implement the feature she is working on. When that developer pushes her code and you pull down the changes, your application will be broke without also modifying the database. The migration file will be able to automatically make the change so you don’t have to try and reverse engineer what has changed.

And thirdly, when you deploy your application to a production server you need a way for the production database to be created or updated. Updating anything in production manually can be a bit hairy as any human error can potentially be disastrous. By using database migrations we can completely remove the human element of updating a production database.

What do Migration files look like?

Last week we ran the Rails generator to create a new Article Model.

bin/rails g model Article title:string body:text

As part of this generation process, Rails also created a migration file:

class CreateArticles < ActiveRecord::Migration
  def change
    create_table :articles do |t|
      t.string :title
      t.text :body

      t.timestamps null: false
    end
  end
end

This migration will create a new database table called articles. We’ve also specified that the table should have a title column of type string and a body column of type text.

Rails will also add a primary key column of id as well as created_at and updated_at timestamps by default.

Understanding the Migration file

The migration file is essentially just a regular Ruby class that is run during the migration process:

class CreateArticles < ActiveRecord::Migration
end

The CreateArticles class inherits from the ActiveRecord::Migration class.

In the change method we have the instructions of what the migration should do:

def change
  create_table :articles do |t|
    t.string :title
    t.text :body

    t.timestamps null: false
  end
end

In this example we are calling the create_table method with an argument of :articles for the table name.

We also pass a block to the method that allows us to specify the names and types of the columns.

Creating Migration files

In the previous example we saw how we can automatically generate a migration file using the model generator command.

However, you will need to generate a migration file whenever you want to alter an existing table or create a join table between two tables.

So how do you generate a standalone migration file?

Rails provides a migration generator command that looks something like this:

bin/rails g migration AddSlugToArticles

This should create the following migration file:

class AddSlugToArticles < ActiveRecord::Migration
  def change; end
end

You can also add the columns you want to add to the table to the generator command:

bin/rails g migration AddSlugToArticles slug:string

This will create the following migration file:

class AddSlugToArticles < ActiveRecord::Migration
  def change
    add_column :articles, :slug, :string
  end
end

As you can see in this example, we are calling the add_column method and passing the table name :articles, the column name :slug and the type :string.

The migration will automatically created with the add_column method if your migration begins with Add.

If you wanted to remove a column from a table, you would generate a migration that begins with Remove:

bin/rails g migration RemoveSlugFromArticles slug:string

This would generate the following migration:

class RemoveSlugFromArticles < ActiveRecord::Migration
  def change
    remove_column :articles, :slug, :string
  end
end

If you would like to create a new table you should run a generator command where the migration begins with Create. For example:

bin/rails g migration CreateComments name:string comment:text

Running the command above would generate the following migration:

class CreateComments < ActiveRecord::Migration
  def change
    create_table :comments do |t|
      t.string :name
      t.text :comment
    end
  end
end

Running Migrations

Once you have generated your migration files and you are happy with the structure of the tables that you have defined, it’s time to run the migrations to update the database.

To run the migration files you can use the following command:

bin/rake db:migrate

This will work through each of your migration files in the order of which they were generated and perform that action on the database. If everything goes smoothly you should see the output from the command console telling you which migrations were run.

If you try to run the db:migrate command again you will notice that nothing will happen.

Rails keeps track of which migrations have already been run. When you run the db:migrate command, Rails will only run the migrations that have yet to be run.

Rolling back migrations

If you have made a mistake and you want to roll back the previous migration you can do so using the following command:

bin/rake db:rollback

For simple migrations Rails can automatically revert the changes from the change method.

If you need to rollback through multiple migrations you can add the STEP parameter to the command:

bin/rake db:rollback STEP=2

This will rollback through the previous 2 migrations.

Conclusion

Migrations are an important part of a modern web application framework. Nearly all web applications have a database, and so it’s pretty important that you have the right tooling.

Migrations make it easy to create database tables. You will no longer have to remember the syntax for creating tables or defining column.

If you were to switch databases, you will also find that the migrations are agnostic to the type of database you are using.

However, really the biggest benefit of migrations is how easy it is to create a new database, or modify an existing one. When working on a team of developers this is going to be essential!