Adding no-default, non-NULL columns to an existing ActiveRecord model
Thursday, June 7, 2012 at 12:57AM
John Carney in Software Development, activerecord, rails, ruby

Recently I had to add a new property to an existing ActiveRecord model. The model represents periodic invoices issued to electricity customers and the new property described the time covered by the invoice in days.

The new property is mandatory, but there is no sensible default that can be filled in for new records. However, for legacy records it was deemed acceptable to set a length of 31 days.

When it comes to writing a migration to add this record, there is a small problem. You can't add a non-NULL column to an existing database table without specifying a default value to use for existing records. I thought about adding the column with a default value, then removing the default later, but initial experimentation showed that you can't use ActiveRecord's change_column to remove a default from a column. So, I ended up writing a migration that added the column without the "not NULL" constraint, populated the existing records, then added the not-NULL constraint.

As you can see, this is fairly inelegant. You could improve it by populating the existing records using an SQL UPDATE statement, but it turns out there is a better way.

Unhappy with the above solution, I looked around a bit more and found that you can remove a column default in ActiveRecord using the change_column_default method. So the way to add a no-default, non-NULL column to an ActiveRecord model is to add the column with a default, then remove the default.

Note that this solution only works if there is a single satisifactory default that can be applied to legacy records. If that is not the case, then you should use the inelegant solution above and replace the logic that fills in the value for existing records.

Article originally appeared on the inner curmudgeon (http://innercurmudgeon.com/).
See website for complete article licensing information.