Altcademy - a Forbes magazine logo Best Coding Bootcamp 2023

What is Database Migration in Ruby on Rails?

In today's fast-paced world of software development, it's crucial to adapt to changing requirements and technologies. One such scenario is evolving your application's database structure to accommodate new features or improve existing ones. This process, known as database migration, is an essential skill for developers, especially when working with Ruby on Rails.

In this blog post, we'll explore the concept of database migration in Ruby on Rails, understand why it's important, and learn how to perform migrations using practical examples. So, let's dive in!

What is Database Migration?

Imagine you're building an application to manage a library's inventory. Initially, you create a table in your database named books, with columns like title, author, and publication_date. Everything works fine, but a few months later, you decide to add a new feature to track each book's genre. To do this, you'll need to add a new column, genre, to your books table.

Database migration is the process of making changes to your application's database schema, like adding or removing tables and columns, changing data types, or modifying indexes. In the context of Ruby on Rails, these changes are managed in a structured and versioned manner using migration files.

Why is Database Migration Important?

Before we dive into the nitty-gritty details of performing migrations in Ruby on Rails, let's quickly discuss why they're essential.

Version Control: Migrations allow you to keep track of changes in your database schema, making it easy to move forward and backward between different versions. This is especially important when multiple developers are working on the same project, as it helps maintain consistency and avoid conflicts.

Reproducibility: Migrations provide a step-by-step guide on how to recreate your database schema from scratch. This makes it easy to set up new environments or onboard new team members without having to manually recreate the database structure.

Data Integrity: When done correctly, migrations ensure that your data remains consistent and accurate even as your database schema changes. This is crucial for maintaining the quality and reliability of your application.

Now that we understand the importance of database migration let's get started with Ruby on Rails.

Migrations in Ruby on Rails

Ruby on Rails provides a powerful and easy-to-use framework for managing database migrations. In this section, we'll walk through the process of creating and applying migrations, as well as rolling them back if needed.

Creating a New Migration

To create a new migration in Ruby on Rails, you can use the rails generate migration command, followed by the migration name. Migration names should be descriptive and indicate the purpose of the migration. For example, to add a genre column to our books table, we can run:

rails generate migration AddGenreToBooks

This will create a new migration file in the db/migrate directory, with a name like 20210608000000_add_genre_to_books.rb (the exact name will depend on the current date and time).

Open the newly created migration file, and you'll see something like this:

class AddGenreToBooks < ActiveRecord::Migration[6.1]
  def change
  end
end

The AddGenreToBooks class inherits from ActiveRecord::Migration, which provides the necessary functionality for managing migrations. The change method is where we'll define the changes we want to make to our database schema.

Defining the Migration

To add the genre column to our books table, we'll use the add_column method inside the change method. We'll also need to specify the data type for our new column. In this case, let's use a string:

class AddGenreToBooks < ActiveRecord::Migration[6.1]
  def change
    add_column :books, :genre, :string
  end
end

Here, add_column takes three arguments: the table name as a symbol (:books), the new column's name as a symbol (:genre), and the column's data type (:string).

You can also use other methods like remove_column, rename_column, create_table, and drop_table to perform different types of migrations. Check the official Rails documentation for more information on available methods and their usage.

Applying the Migration

Once you've defined your migration, it's time to apply it to your database. To do this, run the following command:

rails db:migrate

This will execute the change method in your migration file, effectively adding the genre column to your books table. Rails will also update the schema.rb file in your db directory, which provides an up-to-date representation of your database schema.

Rolling Back a Migration

Sometimes, you might need to undo a migration, either because you made a mistake or because you want to revert to a previous version of your database schema. To do this, you can use the rails db:rollback command:

rails db:rollback

This will undo the last applied migration by running the down method corresponding to the change method in your migration file. In our example, since we used add_column, Rails will automatically generate a down method that uses remove_column to revert the change.

If you want to rollback multiple migrations, you can specify the STEP option:

rails db:rollback STEP=2

This will rollback the last two applied migrations.

Modifying Data in a Migration

In some cases, you might need to modify existing data in your database as part of a migration. For example, let's say you want to split the author column in our books table into two separate columns: first_name and last_name.

First, we'll create a new migration to add the first_name and last_name columns:

rails generate migration AddFirstNameAndLastNameToBooks

Next, we'll define the migration to add the new columns:

class AddFirstNameAndLastNameToBooks < ActiveRecord::Migration[6.1]
  def change
    add_column :books, :first_name, :string
    add_column :books, :last_name, :string
  end
end

Now, before we apply this migration, we need to consider how to handle the existing data in our author column. We can do this by adding an up method to our migration that modifies the data, and a down method that reverts the changes.

Here's an example of how to do this:

class AddFirstNameAndLastNameToBooks < ActiveRecord::Migration[6.1]
  def up
    add_column :books, :first_name, :string
    add_column :books, :last_name, :string

    Book.reset_column_information

    Book.find_each do |book|
      first_name, last_name = book.author.split(' ', 2)
      book.update_columns(first_name: first_name, last_name: last_name)
    end
  end

  def down
    Book.find_each do |book|
      author = "#{book.first_name} #{book.last_name}"
      book.update_columns(author: author)
    end

    remove_column :books, :first_name
    remove_column :books, :last_name
  end
end

In the up method, after adding the new columns, we call Book.reset_column_information to ensure that our Book model is aware of the new columns. Then, we iterate over each book record, split the author into first_name and last_name, and update the record accordingly.

In the down method, we do the reverse: we concatenate the first_name and last_name columns to recreate the author value, and then remove the new columns.

Conclusion

Database migration is a crucial aspect of software development that helps maintain the integrity and consistency of your application's data. Ruby on Rails makes it easy to manage migrations with a structured and versioned approach.

In this blog post, we've explored the basics of database migration in Ruby on Rails, learned how to create, apply, and rollback migrations, and discussed how to modify data as part of a migration. With this knowledge, you'll be well-prepared to handle changes in your application's database schema and ensure that your application remains robust and reliable.