1

I am attempting to add a column to an existing table using an Active Record migration in Rails. I need the column's initial value to be based on other columns in the table. Is there a way to do this in an Active Record migration?

To make it more concrete, let's say I have the following:

my_table
----------------
first_name: text
last_name: text

I want to add a full_name text column with an initial value of concat(first_name, ' ', last_name'. Note that I don't want a default value on the column, as I intend for the application to be populating this going forward (the initial default is just to have a sensible starting value for existing records).

How can I do this in a migration? Ideally I would like to use add_column or similar, though if that can't work a working alternative would be acceptable.

Note that there already exists a nearly identical question (add a database column with Rails migration and populate it based on another column), but none of its answers seem to fully answer this question.

M. Justin
  • 14,487
  • 7
  • 91
  • 130
  • @DanilSperansky It does, though it could be affected by the issue (pointed out in this other answer) when the model has advanced past that point in the migration, which is why I posted an SQL solution to the update. Since it doesn't attempt to instantiate the active record object, the only corner case I can think of where yours would fail would be if there's a migration afterward that renames or deletes the table. – M. Justin Sep 20 '17 at 20:32

2 Answers2

2

You could use update_all after add_column. For MySQL:

Person.update_all('full_name = concat(first_name, " ", last_name)')
Danil Speransky
  • 29,891
  • 5
  • 68
  • 79
  • Per my comment on the question itself, I would imagine this would fail if a subsequent migration deleted or renamed this table, as the active record object with that name would no longer exist to call `update_all` on. In other words, it's not fully future-proof. – M. Justin Sep 20 '17 at 20:33
1

I ended up adding the column using add_column and then using direct SQL to update the value of the column. I used direct SQL and not the model per this answer, since then it doesn't depend on the current state of the model vs. the current state of the table based on migrations being run.

class AddFullName < ActiveRecord::Migration
  def up
    add_column :my_table, :full_name, :text
    execute "update my_table set full_name = concat(first_name, ' ', last_name)"
  end

  def down
    remove_column :my_table, :full_name
  end
end

That said, if there is a better or more idiomatic approach to this, I'm all ears.

M. Justin
  • 14,487
  • 7
  • 91
  • 130