Top shelf web developer training.

Guided Paths

Follow our crafted routes to reach your goals.

Video Courses

Premium videos to build real apps.

Written Tutorials

Code to follow and learn from.

Find Your
Opportunity HIRED
Dismiss
Up

Understanding Migrations in Rails

Related Course

Get Started with JavaScript for Web Development

JavaScript is the language on fire. Build an app for any platform you want including website, server, mobile, and desktop.

Rails provides migrations to help you evolve your database schema in a convenient way. In your new Rails application, your database schema starts off empty. Active Record helps you update it whenever a new migration is run.

Why do you need migrations?

The majority of web applications need a database. A database is used for storing the data of the web application. Typically you would run SQL statements to create or modify your database.

Rails introduces a domain specific language for writing database instructions. This saves you from writing SQL statements

Here is an example of a migration:

class CreateBooks < ActiveRecord::Migration[5.0]
  def change
    create_table :books do |t|
      t.string :title
      t.text :description
      t.string :author

      t.timestamps
    end
  end
end

This migration creates a table called books with a string column called title, a text column called description and another string column called author. A primary key column called id will also be added, as it's the default primary key for all Active Record models. The timestamps macro adds two columns, created_at and updated_at. These special columns are automatically managed by Active Record if they exist.

Creating Migrations

Creating migrations is easy in Rails. Rails provides a generator that makes this as simple as you can expect it to be. Migrations are stored as files in the db/migrate directory, one for each migration class. When creating a new migration, at minimum you need to supply a descriptive name for the migration in CamelCase or underscore text, and the generator does the rest. This is how the command looks like

rails generate migration 

In proper usage, you will have to add a few options:

rails generate migration NAME [field[:type][:index] field[:type][:index]] [options]

You can do something that looks like this:

rails generate migration Products name:string description:text

This will create an empty migration that looks like this:

class Products < ActiveRecord::Migration[5.0]
  def change
  end
end

Note that if you change the classname of your migration to something that doesn’t match its filename, you will get an uninitialized constant error when that migration gets executed.

change

In version 3.1, Rails added the ability to define reversible migrations. In previous versions of Rails, one would have to define two migration instance methods, up and down . The up method included the the logic of what to change in the database, while the down method included the logic on how to revert that change. Using the change method, one only needs to specify the up logic for the majority of use cases. The change method is the primary way of writing migrations. It works for the majority of cases, where Active Record knows how to reverse the migration automatically.

The following migration file illustrates creating a new table named books :

class CreateBooks < ActiveRecord::Migration[5.0]
  def change
    create_table :books do |t|
      t.string :title
      t.text :description
      t.string :author

      t.timestamps
    end
  end
end

As you can see in the example, the migration directive happens within instance method definition, change . If we go to the command line in our project folder and type rake db:migrate, the books table will be created. Rails gives us informative output during the migration process so that we see what is going on:

== 20170118100734 CreateBooks: migrating ======================================
-- create_table(:books)
   -> 0.0052s
== 20170118100734 CreateBooks: migrated (0.0055s) =============================

The change method currently supports only these migration definitions:

  • add_column
  • add_foreign_key
  • add_index
  • add_reference
  • add_timestamps
  • change_column_default (must supply a :from and :to option)
  • change_column_null
  • create_join_table
  • create_table
  • disable_extension
  • drop_join_table
  • drop_table (must supply a block)
  • enable_extension
  • remove_column (must supply a type)
  • remove_foreign_key (must supply a second table)
  • remove_index
  • remove_reference
  • remove_timestamps
  • rename_column
  • rename_index
  • rename_table

Database Schema

The file db/schema.rb is generated every time you migrate and reflects the latest status of your database schema. You should never edit db/schema.rb by hand, because this file is auto-generated from the current state of the database. Instead of editing this file, please use the migrations feature of Active Record to incrementally modify your database, and then regenerate this schema definition.

Note that this schema.rb definition is the authoritative source for your database schema. If you need to create the application database on another system, you should be using db:schema:load, not running all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations you’ll amass, the slower it’ll run and the greater likelihood for issues).

It’s strongly recommended to check this file into your version control system. First of all, it helps to have one definitive schema definition around for reference. Secondly, you can run rake db:schema:load to create your database schema from scratch without having to run all migrations. That’s especially important considering that as your project evolves, it’s likely that it will become impossible to run migrations all the way through from the start, due to code incompatibilities, such as renaming of classes named explicitly.

When we ran the migration to create our books table, our db/schema.rb was updated to look like this:

# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# Note that this schema.rb definition is the authoritative source for your
# database schema. If you need to create the application database on another
# system, you should be using db:schema:load, not running all the migrations
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
# you'll amass, the slower it'll run and the greater likelihood for issues).
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 20170118100734) do

  create_table "books", force: :cascade do |t|
    t.string   "title"
    t.text     "description"
    t.string   "author"
    t.datetime "created_at",  null: false
    t.datetime "updated_at",  null: false
  end

end

Database Related Rake Tasks

Here are few database related tasks that might come in handy.

db:create and db:create:all

Create the database defined in config/database.yml for the current Rails.env . If the current environment is development, Rails will create both the local development and test databases.

db:drop and db:drop:all

Drops the database for the current RAILS_ENV . If the current environment is development, Rails will drop both the local development and test databases.

db:migrate

Applies all pending migrations. If a VERSION environment variable is provided, then db:migrate will apply pending migrations through the migration specified, but no further. The VERSION is specified as the timestamp portion of the migration file name.

Example of migrating up with param

rake db:migrate VERSION=20170118100734
== CreateUsers: migrating ==========================================
-- create_table(:books)
-> 0.0014s
== CreateUsers: migrated (0.0015s) =================================

If the VERSION provided is older than the current version of the schema, then this task will actually rollback the newer migrations.

Example of migrating down with param

rake db:migrate VERSION=20170112152614
== CreateUsers: reverting ==========================================
-- drop_table(:users)
-> 0.0014s
== CreateUsers: reverted (0.0015s) =================================
6.5.0.5 db:migrate:down

db:migrate:down This task will invoke the down method of the specified migration only. The VERSION is specified as the timestamp portion of the migration file name.

rake db:migrate:down VERSION=20170118100734
== CreateClients: reverting ========================================
-- drop_table(:clients)
-> 0.0028s
== CreateClients: reverted (0.0054s) ===============================

db:migrate:up

This task will invoke the up method of the specified migration only. The VERSION is specified as the timestamp portion of the migration file name.

rake db:migrate:down VERSION=20170118100734
== CreateClients: migrating ========================================
-- create_table(:clients)
-> 0.0260s
== CreateClients: migrated (0.0261s) ===============================

db:migrate:redo

Executes the down method of the latest migration file, immediately followed by its up method. This task is typically used right after correcting a mistake in the up method or to test that a migration is working correctly.

rake db:migrate:redo
== AddTimesheetsUpdatedAtToBooks: reverting ========================
-- remove_column(:books, :timesheets_updated_at)
-> 0.0853s
== AddTimesheetsUpdatedAtToBoooks: reverted (0.0861s) ===============
== AddTimesheetsUpdatedAtToBooks: migrating ========================
-- add_column(:books, :timesheets_updated_at, :datetime)
-> 0.3577s
== AddTimesheetsUpdatedAtToBooks: migrated (0.3579s) ===============

Database Seeding

The automatically created file db/seeds.rb is a default location for creating seed data for your database. It was introduced in order to stop the practice of inserting seed data in individual migration files. At its simplest, the contents of seed.rb is simply a series of create! statements that generate baseline data for your application, whether it’s default or related to configuration.

Here is an example of what a seed file looks like:

Country.create({"name"=>"Deutschland", "population"=>81831000})
Country.create({"name"=>"Frankreich", "population"=>65447374})

It can be executed by running rake db:seed.

Conclusion

In this tutorial, we have learned the important aspects of Rails migration. You saw how to write database instructions in your migration file. You can now create and drop tables without writing SQL statements. You also learned about database seeding which is useful for adding seed date to your application. You can dig deeper by going over to Rails Guide on Migration.