By

How to handle data updates in migrations

A couple of weeks ago I was pointed to this article and it turned out I was just in the middle of doing a similar thing : update / move data inside a migration.

As it is indicated if you use defined models in your migrations you will have some surprises in the future in case a model name has changed for whatever reason. Reason is old migrations might not have been also updated properly and then these migrations will fail.

The above post covers different steps of how to improve the process.

Here is how we deal with it at the moment :

First let’s define a basic case. Let’s say we have some customers and these customers have contacts. So some simple models could be :

class Customer < ActiveRecord::Base
  attr_tde :first_name
  attr_tde :last_name
end

class Contact < ActiveRecord::Base
  attr_tde :first_name
  attr_tde :last_name
  attr_tde :email
  attr_tde :mobile_phone
  attr_tde :phone

  belongs_to :customer
end

attr_tde is just a prefix we use for encrypted fields, so in the tables the fields are named like encrypted_first_name for example but we can access Customer#first_name directly.

So the first step is to duplicate the model definition inside the migration file. With a little change, we use a different name for the class and we declare the table_name with the real table name.

I.e :

self.table_name = "customers"

So the migration file becomes something like :

class UpdateDataInCustomerAndInContacts < ActiveRecord::Migration
  class CustomerMigration < ActiveRecord::Base
    self.table_name = "customers"
    attr_tde :first_name
    attr_tde :last_name
  end

  class ContactMigration < ActiveRecord::Base
    attr_tde :first_name
    attr_tde :last_name
    attr_tde :email
    attr_tde :mobile_phone
    attr_tde :phone
    belongs_to :customer
  end

  def up
    # update your data using CustomerMigration and ContactMigration models
  end

  def down
    # revert your data
  end
end

This way, we ensure that the data will migrate correctly even in the future.

But as you certainly noticed, there is a bug. The association between Customer and Contact is no more properly defined. belongs_to :customer makes no sense here.

Therefore we have to precise the associations also in the models defined inside the migration and the file now looks like :

class UpdateDataInCustomerAndInContacts < ActiveRecord::Migration
  class CustomerMigration < ActiveRecord::Base
    self.table_name = "customers"
    attr_tde :first_name
    attr_tde :last_name
  end

  class ContactMigration < ActiveRecord::Base
    attr_tde :first_name
    attr_tde :last_name
    attr_tde :email
    attr_tde :mobile_phone
    attr_tde :phone

    belongs_to :customer,
      class_name: "CustomerMigration",
      foreign_key: "customer_id"
  end

  def up
    # update your data using CustomerMigration and ContactMigration models
  end

  def down
    # revert your data
  end
end

This way we can safely move /update our data and be sure the migration will work also later if the model names change.

Also in your models defined in the migration if needed be sure of course to add your different validations and includes etc…

Jérôme


エンジニア募集中!

ビジネスバンクグループではエンジニアを募集中しています。

弊社が採用しているテクノロジや開発環境に興味を持った方は、 ここから是非エントリー を!