ActiveRecord Migrations
Adding multiple columns to a table
Section titled “Adding multiple columns to a table”To add multiple columns to a table, separate field:type pairs with spaces when using rails generate migration command.
The general syntax is:
rails generate migration NAME [field[:type][:index] field[:type][:index]] [options]For example, the following will add name, salary and email fields to the users table:
rails generate migration AddDetailsToUsers name:string salary:decimal email:stringWhich generates the following migration:
class AddDetailsToUsers < ActiveRecord::Migration[5.0] def change add_column :users, :name, :string add_column :users, :salary, :decimal add_column :users, :email, :string endendAdd a reference column to a table
Section titled “Add a reference column to a table”To add a reference to a team to the users table, run this command:
$ rails generate migration AddTeamRefToUsers team:referencesThis generates the following migration:
class AddTeamRefToUsers < ActiveRecord::Migration[5.0] def change add_reference :users, :team, foreign_key: true endendThat migration will create a team_id column in the users table.
If you want to add an appropriate index and foreign_key on the added column, change the command to rails generate migration AddTeamRefToUsers team:references:index. This will generate the following migration:
class AddTeamRefToUsers < ActiveRecord::Migration def change add_reference :users, :team, index: true add_foreign_key :users, :teams endendIf you want to name your reference column other than what Rails auto generates, add the following to your migration: (E.g.: You might want to call the User who created the Post as Author in the Post table)
class AddAuthorRefToPosts < ActiveRecord::Migration def change add_reference :posts, :author, references: :users, index: true endendRollback migrations
Section titled “Rollback migrations”To rollback the latest migration, either by reverting the change method or by running the down method. Run command:
rake db:rollbackrails db:rollbackRollback the last 3 migrations
Section titled “Rollback the last 3 migrations”rake db:rollback STEP=3rails db:rollback STEP=3STEP provide the number of migrations to revert.
Rollback all migrations
Section titled “Rollback all migrations”rake db:rollback VERSION=0rails db:rollback VERSION=0Add a new column with an index
Section titled “Add a new column with an index”To add a new indexed column email to the users table, run the command:
rails generate migration AddEmailToUsers email:string:indexThis will generate the following migration:
class AddEmailToUsers < ActiveRecord::Migration[5.0] def change add_column :users, :email, :string add_index :users, :email endendRun specific migration
Section titled “Run specific migration”To run a specific migration up or down, use db:migrate:up or db:migrate:down.
Up a specific migration:
rake db:migrate:up VERSION=20090408054555rails db:migrate:up VERSION=20090408054555Down a specific migration:
rake db:migrate:down VERSION=20090408054555rails db:migrate:down VERSION=20090408054555The version number in the above commands is the numeric prefix in the migration’s filename. For example, to migrate to the migration 20160515085959_add_name_to_users.rb, you would use 20160515085959 as the version number.
Redo migrations
Section titled “Redo migrations”You can rollback and then migrate again using the redo command. This is basically a shortcut that combines rollback and migrate tasks.
Run command:
rake db:migrate:redorails db:migrate:redoYou can use the STEP parameter to go back more than one version.
For example, to go back 3 migrations:
rake db:migrate:redo STEP=3rails db:migrate:redo STEP=3Add a new column to a table
Section titled “Add a new column to a table”To add a new column name to the users table, run the command:
rails generate migration AddNameToUsers nameThis generates the following migration:
class AddNameToUsers < ActiveRecord::Migration[5.0] def change add_column :users, :name, :string endendWhen the migration name is of the form AddXXXToTABLE_NAME followed by list of columns with data types, the generated migration will contain the appropriate add_column statements.
Remove an existing column from a table
Section titled “Remove an existing column from a table”To remove existing column name from users table, run the command:
rails generate migration RemoveNameFromUsers name:stringThis will generate the following migration:
class RemoveNameFromUsers < ActiveRecord::Migration[5.0] def change remove_column :users, :name, :string endendWhen the migration name is of the form RemoveXXXFromYYY followed by list of columns with data types then the generated migration will contain the appropriate remove_column statements.
While it’s not required to specify the data type (e.g. :string) as a parameter to remove_column, it is highly recommended. If the data type is not specified, then the migration will not be reversible.
Running migrations in different environments
Section titled “Running migrations in different environments”To run migrations in the test environment, run this shell command:
rake db:migrate RAILS_ENV=testStarting in Rails 5.0, you can use rails instead of rake:
rails db:migrate RAILS_ENV=testCreate a new table
Section titled “Create a new table”To create a new users table with the columns name and salary, run the command:
rails generate migration CreateUsers name:string salary:decimalThis will generate the following migration:
class CreateUsers < ActiveRecord::Migration[5.0] def change create_table :users do |t| t.string :name t.decimal :salary end endendWhen the migration name is of the form CreateXXX followed by list of columns with data types, then a migration will be generated that creates the table XXX with the listed columns.
Running migrations
Section titled “Running migrations”Run command:
rake db:migraterails db:migrateSpecifying target version will run the required migrations (up, down, change) until it has reached the specified version. Here, version number is the numerical prefix on the migration’s filename.
rake db:migrate VERSION=20080906120000rails db:migrate VERSION=20080906120000Change an existing column’s type
Section titled “Change an existing column’s type”To modify an existing column in Rails with a migration, run the following command:
rails g migration change_column_in_tableThis will create a new migration file in db/migration directory (if it doesn’t exist already), which will contain the file prefixed with timestamp and migration file name which contains the below content:
def change change_column(:table_name, :column_name, :new_type)endRails Guide – Changing Columns
A longer but safer method
Section titled “A longer but safer method”The above code prevents the user from ever rolling back the migration. You can avoid this problem by splitting the change method into separate up and down methods:
def up change_column :my_table, :my_column, :new_typeend
def down change_column :my_table, :my_column, :old_typeendAdd column with default value
Section titled “Add column with default value”The following example adds a column admin to the users table, and gives that column the default value false.
class AddDetailsToUsers < ActiveRecord::Migration[5.0] def change add_column :users, :admin, :boolean, default: false endendMigrations with defaults might take a long time in large tables with for example PostgreSQL. This is because each row will have to be updated with the default value for the newly added column. To circumvent this (and reduce downtime during deployments), you can split your migration into three steps:
- Add a
add_column-migration similar to the one above, but set no default - Deploy and update the column in a rake task or on the console while your app is running. Make sure your application already writes data to that colum for new/updated rows.
- Add another
change_columnmigration, which then changes the default of that column to the desired default value
Create a join table
Section titled “Create a join table”To create a join table between students and courses, run the command:
$ rails g migration CreateJoinTableStudentCourse student courseThis will generate the following migration:
class CreateJoinTableStudentCourse < ActiveRecord::Migration[5.0] def change create_join_table :students, :courses do |t| # t.index [:student_id, :course_id] # t.index [:course_id, :student_id] end endendCreate a hstore column
Section titled “Create a hstore column”Hstore columns can be useful to store settings. They are available in PostgreSQL databases after you enabled the extension.
class CreatePages < ActiveRecord::Migration[5.0] def change create_table :pages do |t| enable_extension 'hstore' unless extension_enabled?('hstore') t.hstore :settings t.timestamps end endendAdd a self reference
Section titled “Add a self reference”A self reference can be useful to build a hierarchical tree. This can be achieved with add_reference in a migration.
class AddParentPages < ActiveRecord::Migration[5.0] def change add_reference :pages, :pages endendThe foreign key column will be pages_id. If you want to decide about the foreign key column name, you have to create the column first and add the reference after.
class AddParentPages < ActiveRecord::Migration[5.0] def change add_column :pages, :parent_id, :integer, null: true, index: true add_foreign_key :pages, :pages, column: :parent_id endendCreate an array column
Section titled “Create an array column”An array column is supported by PostgreSQL. Rails will automatically convert a PostgreSQL array to a Ruby array, and vice-versa.
Create a table with an array column:
create_table :products do |t| t.string :name t.text :colors, array: true, default: []endAdd an array column to an existing table:
add_column :products, :colors, array: true, default: []Add an index for an array column:
add_index :products, :colors, using: 'gin'Changing Tables
Section titled “Changing Tables”If you have created a table with some wrong schema, then the easiest way to change the columns and their properties is change_table. Review the following example:
change_table :orders do |t| t.remove :ordered_at # removes column ordered_at t.string :skew_number # adds a new column t.index :skew_number #creates an index t.rename :location, :state #renames location column to stateendThe above migration changes a table orders. Here is a line-by-line description of the changes:
t.remove :ordered_atremoves the columnordered_atfrom the tableorders.t.string :skew_numberadds a new string-type column namedskew_numberin theorderstable.t.index :skew_numberadds an index on theskew_numbercolumn in theorderstable.t.rename :location, :staterenames thelocationcolumn in theorderstable tostate.
Add an unique column to a table
Section titled “Add an unique column to a table”To add a new unique column email to users, run the following command:
rails generate migration AddEmailToUsers email:string:uniqThis will create the following migration:
class AddEmailToUsers < ActiveRecord::Migration[5.0] def change add_column :users, :email, :string add_index :users, :email, unique: true endendChecking migration status
Section titled “Checking migration status”We can check the status of migrations by running
rake db:migrate:statusrails db:migrate:statusThe output will look like this:
Status Migration ID Migration Name--------------------------------------------------up 20140711185212 Create documentation pagesup 20140724111844 Create nifty attachments tableup 20140724114255 Create documentation screenshotsup 20160213170731 Create ownersup 20160218214551 Create usersup 20160221162159 ********** NO FILE **********up 20160222231219 ********** NO FILE **********Under the status field, up means the migration has been run and down means that we need to run the migration.
Forbid null values
Section titled “Forbid null values”To forbid null values in your table columns, add the :null parameter to your migration, like this:
class AddPriceToProducts < ActiveRecord::Migration def change add_column :products, :float, null: false endendAdding a NOT NULL constraint to existing data
Section titled “Adding a NOT NULL constraint to existing data”Say you want to add a foreign key company_id to the users table, and you want to have a NOT NULL constraint on it. If you already have data in users, you will have to do this in multiple steps.
class AddCompanyIdToUsers < ActiveRecord::Migration def up # add the column with NULL allowed add_column :users, :company_id, :integer
# make sure every row has a value User.find_each do |user| # find the appropriate company record for the user # according to your business logic company = Company.first user.update!(company_id: company.id) end
# add NOT NULL constraint change_column_null :users, :company_id, false end
# Migrations that manipulate data must use up/down instead of change def down remove_column :users, :company_id endendParameters
Section titled “Parameters”|Column type|Description
|---|---|---|---|---|---|---|---|---|---
|:primary_key|Primary key
|:string|Shorter string datatype. Allows limit option for maximum number of characters.
|:text|Longer amount of text. Allows limit option for maximum number of bytes.
|:integer|Integer. Allows limit option for maximum number of bytes.
|:bigint|Larger integer
|:float|Float
|:decimal|Decimal number with variable precision. Allows precision and scale options.
|:numeric|Allows precision and scale options.
|:datetime|DateTime object for dates/times.
|:time|Time object for times.
|:date|Date object for dates.
|:binary|Binary data. Allows limit option for maximum number of bytes.
|:boolean|Boolean