Rails 7.1 allows 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, validate_constraint and validate_check_constraint had to be called outside the change_table block, requiring constraint creation and validation to be handled as two separate 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

With the enhancement introduced in Rails 7.1, we can now create 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

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