Blog

Thoughts from my daily grind

Add & Remove Model NOT NULL Constraint - Rails Migration

Posted by Ziyan Junaideen |Published: 03 April 2021 |Category: Ruby on Rails
Default Upload |

My style of coding keeps models slim preferably with no business logic (including validation) and database tables without constraints the likes of NOT NULL, CHECK and EXCLUDE where data integrity is not of a a concern. These are things I usually delegate to relevant form objects. Call it a bad habit, but especially with relations I would like to keep the possibility to nullifying it when needed.

On Create Table

You might be wanting to make table (new model) with a field not nullable with the null: false option to make a field not nullable or null: true to make make nullable.

class CreateOrders < ActiveRecord::Migration[6.1]
  def change
    create_table :orders do |t|
      # ...

      t.references :cart, null: false, foreign_key: true
      t.references :user, null: true, foreign_key: true

      # ...
    end
  end
end

This will create a cart_id and user_id field in the orders table that are not only foreign keys (when not null needs to adhere to referential integrity) but also nut nullable.

On Existing Table

If you have an existing table, you will need to use the add_column for a new column that is constrained by the null: false option. If you wan to update an existing column you will have to use the change_column_null method.

# New column
add_column :orders, :cart_id, :bigint, null: false
add_column :orders, :user_id, :bigint, null: true

# Change column
change_column_null :orders, :cart_id, false
change_column_null :orders, :user_id, true

Default Values

Before you are making a column NOT NULL you would want to make user there are no cells that are null or you risk running in to an exception. You can do this by providing a default value or manually setting the value using a loop.

class MakeOrderReferenceNotNull < ActiveRecord::Migration[6.1]
  def up
    Order.where(reference: nil).each do |order|
      order.update(reference: OrderReferenceGenerator.for(order))
    end

    change_column_null :orders, :reference, false
  end
end

or you can add a default value to the change_column_null method.

class MakeOrderReferenceNotNull < ActiveRecord::Migration[6.1]
  def up
    change_column_null :orders, :reference, false, Time.zone.now.to_i
  end
end

Rails 3

The change_column_null has been with ActiveRecord since Ruby on Rails 4. In case you are rocking an old Ruby on Rails project (which you shouldn't) you will have to use the change_column as...

class MakeOrderReferenceNotNull < ActiveRecord::Migration
  def change
    Order.where(reference: nil).each do |order|
      order.update(reference: OrderReferenceGenerator.for(order))
    end

    change_column :orders, :reference, :string, null: false
  end
end
Tags
About the Author

Ziyan Junaideen -

Ziyan is an expert Ruby on Rails web developer with 8 years of experience specializing in SaaS applications. He spends his free time he writes blogs, drawing on his iPad, shoots photos.

Comments