Resetting ActiveRecord object columns in migrations
Posted by #
Often when you use migrations to keep track of your database schema, you also need to migrate data around in the database. You can use the execute method to do this in pure SQL, but since migrations are Ruby code and have access to all the ActiveRecord code, why not do it in there? Here’s an example from our latest project where I reverse a has_one—belongs_to association:
def self.up
add_column :images, :content_id, :integer
Content.find(:all, :conditions => "image_id is not null").each do |entry|
image = Image.find_by_id(entry.image_id) rescue nil
if image
image.content_id = entry.id
image.save
end
end
remove_column :contents, :image_id
end
However, this has one problem. The Image class is loaded and the column information cached before the column content_id is added, so it doesn’t have the getter and setter methods for that column and thus image.content_id = entry.id will bomb.
Fortunately this is easy enough to fix. Just call Classname.reset_column_information after the column is added, and the new column will be found:
def self.up
add_column :images, :content_id, :integer
Image.reset_column_information
Content.find(:all, :conditions => "image_id is not null").each do |entry|
image = Image.find_by_id(entry.image_id) rescue nil
if image
image.content_id = entry.id
image.save
end
end
remove_column :contents, :image_id
end

It’s usualy better to use SQL because down the road you may change the image classs in such a way that it no longer will work with the old data. (If you add a vaildation in the future on a field that isn’t even created in this migration, for example)
Daniel,
You’re right, I just noticed the same in some other context. So the example is not a very good one.
However, there are cases where you need to work on the class level and in those cases
reset_column_informationis very useful.It’s just not very well documented, so I wanted to give some time in the limelights for it :-)
I ran into this gotcha too and was going to blog about it but you beat me to it. Note that you can redefine your ActiveRecord classes to protect against class changes, see http://rails.techno-weenie.net/tip/2006/2/23/safely_using_models_in_migrations