scttnlsn
scttnlsn

Reputation: 3026

Accessing raw ActiveRecord data for Rails migration

I'd like to migrate some of the columns of my user table into a separate related table. A few of these columns use ActiveRecord's serialize functionality to store hash and set data in a single column.

class User < ActiveRecord::Base
  ...
  serialize :foo, Hash
  ...
end

I'd like to move that column (in addition to many others) into a separate table:

class Profile < ActiveRecord::Base
  ...
  serialize :foo, Hash

  belongs_to :user
  ...
end

As part of the migration I copy over a bunch of user attributes into the profile before dropping the user columns. The problem is when I read user.foo the data is no longer deserialized. So when I set profile.foo = user.foo I get an error: Attribute was supposed to be a Hash, but was a String.

Is there a way to copy the raw database values during the migration or do I need to perform my own deserialization in this case?

Thanks!

EDIT: Adding migration

class CreateProfile < ActiveRecord::Migration
  def up
    create_table :profiles do |t|
      t.integer :user_id
      t.text :foo

      # A bunch of other fields
    end

    User.all.find_each do |user|
      params = { user: user }

      # Copy all fields here
      params[:foo] = user[:foo]
      ...

      profile = Profile.new(params)
      profile.save!
    end

    # Remove a bunch of fields
    remove_column :users, :foo
    ...
  end

  ...
end

Upvotes: 0

Views: 989

Answers (2)

PinnyM
PinnyM

Reputation: 35533

It appears that you are removing the serialize :foo, Hash line in your User model with the anticipation that this column will be dropped. Because of this, you run into the problem you described. You can either:

  • Leave that line in place and remove it at a later point after all migrations have run. And that means all migrations for all existing environments for your codebase (or they will later break as @scttnlsn has pointed out). A much simpler approach would be...

  • Use a SQL 'UPDATE' statement to copy the data:

Assuming you have created the profiles in advance, you can update them like so:

execute("UPDATE profiles SET foo = users.foo FROM users WHERE user_id = users.id")

If you are creating these profile records on the fly during this migration, use an INSERT query similar to the one that @Vimsha has proposed.

Upvotes: 1

usha
usha

Reputation: 29349

You could execute raw sql in migration which will be the fastest if you have large number of user records

execute("INSERT INTO profiles(user_id, foo) SELECT id, foo FROM users")

Upvotes: 2

Related Questions