Bulk Migrations by Passing validate(_check)_constraint through change_table

When we are running Rails migrations, it’s common to combine multiple changes into a single change_table block especially when we want to keep database schema in sync.

Before Rails 7.1

Previously the validate_constraint and validate_check_constraint was called outside of the change_table block to check constraint and its validations into two distinct steps

For example:

class AddNameColumnsAndConstraintToUser < ActiveRecord::Migration[7.0]
  def change
    change_table :user, bulk: true do |t|
      t.string :first_name
      t.string :last_name
      t.check_constraint "first_name IS NOT NULL AND last_name IS NOT NULL",
        name: "name_not_null", validate: false
    end

    validate_check_constraint :user, "name_not_null"
  end
end

After

Rails 7.1 added the enhancement, we can now write cleaner migrations by combining column additions and constraint validations within a single change_table block.

def validate_constraint(*args)
  @base.validate_constraint(name, *args)
end

def validate_check_constraint(*args)
  @base.validate_check_constraint(name, *args)
end

With this change, we can write cleaner migrations by combining column additions and constraint validations within a single change_table block.

For example:

class AddUserDetailsToUsers < ActiveRecord::Migration[7.0]
  def change
    change_table :users, bulk: true do |t|
      t.string :first_name
      t.string :last_name
      t.check_constraint "first_name IS NOT NULL AND last_name IS NOT NULL",
        name: "name_not_null"

      # Now Rails knows to delegate this method to validate the constraint
      t.validate_check_constraint "name_not_null"
    end
  end
end

Need help on your Ruby on Rails or React project?

Join Our Newsletter