If you have an existing model in your Ruby on Rails application and you’ve already run the migration to create the table in the database, you may want to add columns later on. This is easy. Let’s say we have an ActiveRecord user model, and we want to add a rating for each user, to allow others to vote a user up or down. First, create the migration:
script/generate migration AddRatingToUsers
Or if you want to be fancy, we can use a little magic and specify our new column along with it:
script/generate migration AddRatingToUsers rating:integer
Rails is smart enough to figure out the table name from the migration name, and you’ll see this in the migration file that is created:
class AddRatingToUsers < ActiveRecord::Migration
def self.up
add_column :users, :rating, :integer
end
def self.down
remove_column :users, :rating
end
end
If you run the migration right away, you’ll run into problems because all the users that exist will have a rating of nil, and this will probably break any calculations you have in your code. Let’s update it with a default value:
add_column :users, :rating, :integer, :default => 0
Now the database will default all existing and new users to a rating of zero.
More Complex Default Values
But what if you need a more complex default? Let’s say you want to add a unique API key for each user. Maybe we have a user instance method called “generate_api_key”, and it’s called whenever a new user is created:
class User < ActiveRecord::Base
after_create :generate_api_key
def generate_api_key
self.update_attribute(:api_key, self.username + '123')
end
end
But what about the users that were already in the database when we added the column? Should we put that in the migration, too? Absolutely not. It can be done, but migrations are not meant for data loading – only creating and changing the structure of your underlying database.
A better way is a rake task. These belong in the lib/tasks folder of your application, and they’re easy to create:
# lib/tasks/invitation_tokens.rake
namespace :seed do
desc "generate api keys for users that don't have one already"
task :api_keys => :environment do
User.all(:conditions => {:api_key => nil}).each do |user|
user.generate_api_key
end
end
end
Then, you can call this rake task from the command line like this:
rake seed:api_keys
This has a couple benefits. First, if you setup a new instance of your rails app on a different server (or even somewhere else on the same server) you can call this rake task to jumpstart your users. Even better, since you’re relying on a model method, you can unit test that method to make sure it performs as expected.
Tags: columns, Database, developers, migrations, models, Rails, Ruby, ruby on rails, seed data
January 25, 2010 at 5:20 pm |
Thanks Jamie. This takes out a lot of confusion I had before.
July 24, 2010 at 11:28 am |
I’m curious… I need to add some columns to my County model. I’m tracking population statistics and recently was given the requirement to track race statistics, too. There is A LOT of data. I already have a row in the database for each county in the US. How do I add columns and the data specific to them? If I have the data in a .csv file with the county ID as one of the columns, can I seed and require a match to that column?
April 15, 2011 at 8:45 pm |
Thanks Jamie. Fixed my error :)