Create or remove columns or tables with Rails migrations

Here's how to go about creating or removing a column optionally using Rails migrations

A feature that Rails 6.1 added was the ability to use :if_not_exists and :if_exists. This allow us to generate or remove a column or table if it was or wasn’t already present in the database.

It may seem strange that you would want to use methods like this. Surely a glance at your schema.rb file would tell you what tables and columns you have in your database? While this is true sometimes your database can get in a state where the migration tables are out of sync because you’ve been switching branches frequently and are out of sync with other developers on your team. You may also be in the situation where you have had to restore from a database backup and it mightn’t be in sync with what you’re working with now.

Using :if_not_exists

This will create the Articles table if it didn’t already exist in your system:

class CreateArticles < ActiveRecord::Migration[6.1]
  def change
    create_table :articles, if_not_exists: true do |t|
      t.string :title
      t.text :body

      t.timestamps
    end
  end
end

This will create the featured column on the Discounts table if it didn’t already exist:

class AddFeaturedToDiscounts < ActiveRecord::Migration[6.1]
  def change
    add_column :discounts, :featured, :boolean, default: false, if_not_exists: true
  end
end

Using :if_exists

Conversely you can use :if_exists when removing a column (or a table):

class RemoveSubtitleFromArticles < ActiveRecord::Migration[6.1]
  def change
    remove_column :articles, :subtitle, :text, if_exists: true
  end
end 

Before Rails 6.1

We would’ve had to use column_exists? like so:

class RemoveSubtitleFromArticles < ActiveRecord::Migration[5.1]
  def change
    if ActiveRecord::Base.connection.column_exists?(:articles, :subtitle)
      remove_column :articles, :subtitle
    end
  end
end

You can see how this is not as neat as the example given above.

Or to check for a table existing:

class DropArticles < ActiveRecord::Migration[5.1]
  def change
    if ActiveRecord::Base.connection.table_exists?(:articles)
      drop_table :articles do |t|
        t.string :title
        t.text :body

        t.timestamps
      end
    end
  end
end

This article is a part of the "Rails migrations" series

Recent posts View all

Freelancing

Getting the most out of your agency

Here are some tips based on years of working with, for, and as an agency on how to get the most out of any work you do.

VS Code Web Dev

Select multiple lines in VS Code

How to select multiple lines to edit at once within VS Code