ActiveRecord Migrations

Flashcard 3 of 6

We want to add a new column to mark users as admins. Since we want to avoid having null values for this boolean field, we need to backfill the value for all existing users.

Are there any potential issues with the following migration?

class AddAdminFlagToUsers < ActiveRecord::Migration
  def up
    add_column :users, :admin, :boolean, default: false
    User.update_all(admin: false)
    change_column_null :users, :admin, false
  end

  def down
    remove_column :users, :admin
  end
end

We are referencing a User constant which may not exist when this migration is run in the future.

While migrations contain the full history of the database schema for a project, they are always run in the context of the current codebase. Referencing a model constant, while tempting, can lead to issues down the road.

Instead, you should use SQL directly to make the change since the table that the model wraps is guaranteed to be present when the migration is run, unlike the model itself.

class AddAdminFlagToUsers < ActiveRecord::Migration
  def up
    add_column :users, :admin, :boolean, default: false

    connection.update(<<-SQL)
      UPDATE users SET admin = 'f'
    SQL

    change_column_null :users, :admin, false
  end

  def down
    remove_column :users, :admin
  end
end

The updated version of the migration uses the connection object to execute a raw SQL statement directly against the database, additionally using a [heredoc][] to define the SQL statement.

Note - we also need to create the column in two steps since it can't be created with null: false as the existing rows will not pass this constraint.

[heredoc]: http://ruby-doc.org/core-2.2.0/doc/syntax/literals_rdoc.html#label-Here+Documents

Return to Flashcard Results