Rails 6.1 adds support for check constraints to database migrations


For adding check constraints until now with Rails, we had to execute raw SQL queries using migrations.

Example

Suppose we want to create a table for Book model with a check constraint on price field that price should be greater than 100. The only way to do this earlier was to write a migration with a raw sql query after creating table as given below:

class CreateBooks < ActiveRecord::Migration
  def change
    create_table :books do |t|
      t.string :name
      t.integer :price
    end
  end
end
class AddConstraintToBooks < ActiveRecord::Migration
  def up
    execute "ALTER TABLE books ADD CONSTRAINT price_check CHECK (price > 100)"
  end

  def down
    execute "ALTER TABLE books DROP CONSTRAINT price_check"
  end
end

Moreover, this was an irreversible migration hence had to add it separately with up and down methods.

Solution? check_constraint, :add_check_constraint and remove_check_constraint

Rails 6.1 now allows adding check_constraint to rails migration when creating table as well as DSL to create check_constraints via migration after the table has been created.

Syntax for adding check_constraint at the time of table creation is:

create_table :table_name do |t|
  ...
  t.check_constraint [constraint_name], [constraint]
end

Syntax for adding and removing check_constraint to an already existing table is:

add_check_constraint :table_name, :constraint_condition, name: "constraint_name"
remove_check_constraint :table_name, name: "constraint_name"

Notice that both add_check_constraint and remove_check_constraint are reversible.

Example:

In the previous Book example.

Notice that we are adding check_constraint when creating the books table itself:

class CreateBooks < ActiveRecord::Migration
  def change
    create_table :books do |t|
      t.string :name
      t.integer :price
      t.check_constraint "price_check", "price > 100" 
    end
  end
end

For adding a price_check constraint to books table through a separate migration:

class CreateBooks < ActiveRecord::Migration
  def change
    add_check_constraint :books, "price > 100", name: "price_check"
  end
end

And for removing the price_check constraint from books table through a separate migration:

class CreateBooks < ActiveRecord::Migration
  def change
    remove_check_constraint :books, name: "price_check"
  end
end