Adding new field to pre-existing table and modifying previously created data in Rails
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,
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".
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.
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
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:
- Write your up and down migration if possible, if not, raise IrreversibleMigration
- Test it. :)
- See: Can Rails Migrations be used to convert data?
精彩评论