Reputation: 3026
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
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
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