A complex migration may involve processes that Active Record does not know how to reverse. You can use reversible to specify what to do when a migration is run and when it needs to be rolled back. For example:
class ExampleMigration < ActiveRecord::Migration
def change
create_table :distributors do |t|
t.string :zipcode
end
do |dir|
dir up do
# add a CHECK constraint
execute<<-SQL
ALTER TABLE distributors
ADD CONSTRAINT zipchk
CHECK (char_length(zipcode) = 5) NO INHERIT;
SQL
end
dir.down do
execute<<-SQL
ALTER TABLE distributors
DROP CONSTRAINT zipchk
SQL
end
end
add_column :users, :home_page_url, :string
rename_column :users, :email, :email_address
end
end
Using reversible ensures that the instructions are executed in the correct order. If the previous migration example is rolled back, the down block will run after the home_page_url column is dropped and before the distributors table is dropped.
Sometimes a migration will do things that are simply irreversible; for example, it may destroy some data. In such cases, you can call ActiveRecord::IrreversibleMigration in your down block. If anyone tries to undo your migration, an error will be displayed stating that it can’t be done.
Using up/down methods
You can also use the old style of migrations using the up and down methods instead of change. The up method should describe the changes you want to make to your schema, and the down method of your migration should reverse the changes made by the up method. In other words, the database schema must remain the same after executing up and then down. For example, if you created a table in the up method, it should be deleted in the down method. It is reasonable to undo changes in the exact opposite order to the one in which they were made in the up method. Then the example from the reversible section would be equivalent to:
class ExampleMigration < ActiveRecord::Migration[5.0]
def up
create_table :distributors do |t|
t.string :zipcode
end
#add a CHECK constraint
execute<<-SQL
ALTER TABLE distributors
ADD CONSTRAINT zipchk
CHECK (char_length(zipcode) = 5);
SQL
add_column :users, :home_page_url, :string
rename_column :users, :email, :email_address
end
def down
rename_column :users, :email_address, :email
remove_column :users, :home_page_url
execute<<-SQL
ALTER TABLE distributors
DROP CONSTRAINT zipchk
SQL
drop_table :distributors
end
end
If your migration is not reversible you should call ActiveRecord::IrreversibleMigration from your down method. If anyone tries to undo your migration, an error will be displayed stating that it can’t be done.
(reverting-previous-migrations) Reverting to previous migrations
You can use the Active Record feature to roll back migrations using the revert method:
require_relative ‘20121212123456_example_migration’
class FixupExampleMigration < ActiveRecord::Migration[5.0]
def change
revert ExampleMigration
create_table(:apples) do |t|
t.string :variety
end
end
end
The revert method can also accept a block. This can be useful for rolling back a selected part of previous migrations. As an example, let’s imagine that ExampleMigration is committed, and later we decide that it would be better to use Active Record validations, instead of the CHECK constraint, to check the zipcode.
A similar migration could also be written without using revert, but that would involve a few more steps: reordering create table and reversible, replacing create_table with drop_table, and eventually changing up to down and vice versa. Revert has already taken care of all this.
NOTE: If you want to add CHECK constraints like in the examples above, you must use structure.sql as the export method. See Export Schema.
Running migrations
Rails provides a number of bin/rails tasks to run specific sets of migrations.
The very first bin/rails task migration we’ll be using is rails db:migrate. In its main form, it just runs the change or up method on all migrations that haven’t been run yet. If there are no such migrations, it exits. It will run these migrations in order based on the date of the migration.
Note that running the db:migrate task also invokes the db:schema:dump task, which updates your db/schema.rb file to match your database structure.
If you specify a target version, Active Record will run the required migrations (up, down, or change methods) until it reaches the required version. Version is a numeric prefix for the migration file. For example, to migrate to version 20080906120000, run:
$ bin/rails db:migrate VERSION=20080906120000
If version 20080906120000 is greater than the current version (i.e. forward migration) this will run the change (or up) method on all migrations up to and including 20080906120000, but will not run any later migrations. If the migration is backward, this will run the down method on all migrations up to, but not including, 20080906120000.
Rollback
A common task is to rollback the last migration. For example, you made a mistake and want to fix it. You can track down a version of a previous migration and migrate to it, but you can do it easier by running:
$ bin/rails db:rollback
This will revert the situation to the last migration, either by reversing the change method or by running the down method. If you need to undo multiple migrations, you can specify the STEP option:
$ bin/rails db:rollback STEP=3
there will be a rollback to the last 3 migrations.
The db:migrate:redo task is a shortcut to perform a rollback and then run the migration again. Just as with the db:rollback task, you can specify the STEP option if you need to work with more than one version, for example:
$ bin/rails db:migrate:redo STEP=3
None of these bin/rails tasks can do anything that can’t be done with db:migrate. They’re just more convenient because you don’t have to explicitly specify which migration version to migrate to.
Database installation
The rails db:setup task will create the database, load the schema, and initialize it with the seed data.
Reset database
The rails db:reset task will remove the database and reinstall it. This is functionally equivalent to rails db:drop db:setup.
NOTE. This is not the same as running all migrations. It only uses the current contents of the db/schema.rb or db/structure.sql file. If the migration cannot be rolled back, rails db:reset may not help you. For more information about exporting a schema, see Exporting a Schema.
Running specific migrations
If you need to run a specific migration (up or down), the db:migrate:up and db:migrate:down tasks will do that. Just define the appropriate option and the appropriate migration will have its change, up or down method called, like so:
$ bin/rails db:migrate:up VERSION=20080906120000
will run the up method on migration 20080906120000. This task will first check if the migration has already been run, and will do nothing if Active Record thinks it has already run.
Running migrations in different environments
By default, running bin/rails db:migrate will run in the development environment. To run migrations in a different environment, it can be specified using the RAILS_ENV environment variable when running the command. For example, to run migrations in the test environment, you would run:
$ bin/rails db:migrate RAILS_ENV=test
Changing the output of running migrations
By default, migrations only tell us what they do and how long it took. A migration that creates a table and adds an index will output something like this:
== CreateProducts: migrating =========================================== ====
— create_table(:products)
-> 0.0028s
== CreateProducts: migrated (0.0028s) =======================================
Some methods in migrations allow you to control all this:
Method Purpose
suppress_messages Takes a block as an argument and suppresses any output generated by this block.
say Takes a message as an argument and prints it as is. A second boolean argument can be passed to indicate whether indentation is needed or not.
say_with_time Prints the text along with the duration of the block. If the block returns a number, it is assumed to be the number of affected rows.