views:

197

answers:

4

So I've a user model and a corresponding table (users) with fields: User_id, username, password etc.

But then I added invitation_limit to users table. Newly created users have invitation_limit, however the users created before the invitation_token migration was run don't have the invitation_limit field.

How do I update the previously created users to include invitation_token field? Mind you I'm not talking about adding invitation_limit field to users table (i.e. migration), but rather updating already created users.

Thanks,

+1  A: 

The generally accepted strategy for this, unfortunately, is to add another migration which populates those fields. Of course, you can't use active record (or your migrations might break), so best practice is to do this in pure SQL.

floyd
I see. Does this work? User.find(:all).each do |p| p.update_attribute :invitation_limit, 5 end
Senthil
That will work, but it is not best practice as I mentioned in my answer because it references a model which, strictly speaking, could be removed or renamed in the future causing your migrations to fail.
floyd
Thanks, I'll use the rake task mentioned above.
Senthil
A: 

You want to use the migration for this. The migration is just ruby code, so you can access all the data that your application has access to. Alter data, add missing data, delete rows, etc.

Some quick tips:

dpb
+3  A: 

Seed data shouldn't be put in a migration. Migrations are for creating structure, not adding data, and you'll run into problems if you ever move the database.

You could write a rake task. If you create an instance method called something like "generate_invitation_token", you can then write a rake task like this:

# lib/tasks/invitation_tokens.rake
namespace :seed do
  desc "generate invitation tokens for users that don't have one already"
  task :user_tokens => :environment do
    User.all(:conditions => {:invitation_token => nil}).each do |user|
      user.generate_invitation_token
    end
  end
end

You can then call the rake task from the commandline like this:

rake seed:user_tokens

This is a cleaner way to do it.

UPDATE

I've expanded this answer in a blog post called "Adding Columns and Default Data To Existing Models".

Jaime Bellmyer
That's exactly what I was looking for, thanks!
Senthil
+1  A: 

What I understand is that you want to update User records, not to create seed (seed adds data to your database, for example Roles ans so on, which are important to your application). From documentaion:

class AddReceiveNewsletterToUsers < ActiveRecord::Migration
  def self.up
    change_table :users do |t|
      t.boolean :receive_newsletter, :default => false
    end
    User.update_all ["receive_newsletter = ?", true]
  end

  def self.down
    remove_column :users, :receive_newsletter
  end
end

You can also use your models inside migration:

User.all(:conditions => {}).each do |user|
  user.do_sth
  user.save
end

and so on, read about caveats

Edit:
After your comment, this is how I think you should do it in migration;

class AddInvitationLimitToUser < ActiveRecord::Migration
  def selt.up
    change_table :users do |t|
      t.integer :invitation_limit # and add validation to model `validates_presence_of` and so on
    end
    # now update all records already in database, which didn't had value in invitation_limit column
    User.update_all ["invitation_limit = ?", 5]
  end

  def self.down
    remove_column, :users, :invitation_limit
  end
end
MBO
Well I want to add a new field to my User table after the user has been created. I created user model, then added invitation_limit field to users table, so the users created before the migration of new field won't have the new field. I want to update those users and add invitation_limit and set them to 5.
Senthil
@Senthil Users create before migration of new field will have the new field, they only don't have value in it (it'll be NULL by default). They're in same table, which you already modified, so they can't miss new field (column) only because the were created earlier.
MBO
You're right, it does get created. I guess the error I was getting before was it saying it was NULL and not it wasn't created. Thanks!
Senthil