valibuk.net

A place for opinions and notes of a valibuk.

Using Migrations for Generating Initial or Test Data

Tagged with: — ondrej at 10:49 pm on Wednesday, July 5, 2006

Ruby on RailsThe migration functionality allows not only to create (and modify) tables and their columns but also to enter initial (or test) data.

Firstly we will create a simple example (only one table); in the second example we will create two joined tables and show how to generate data for them.

In general, for each table there will be two migrations — the first one creates a table and its columns, the second one inserts data.


One Table

Let’s create a model similar to the User model in the Depot application from the Agile Web Development with Rails book:

ruby script/generate model user

You should see the following output:

  1. exists  app/models/
  2. exists  test/unit/
  3. exists  test/fixtures/
  4. create  app/models/user.rb
  5. create  test/unit/user_test.rb
  6. create  test/fixtures/users.yml
  7. exists  db/migrate
  8. create  db/migrate/001_create_users.rb

The generated migration 001_create_users.rb contains the default structure:

  1. class CreateUsers < ActiveRecord::Migration
  2.   def self.up
  3.   end
  4.  
  5.   def self.down
  6.   end
  7. end

Our user model should contain the following items: login name and encrypted password. And because we like the elegance of RoR we will also add the created_on and updated_on fields to save when a record was created and updated.
The modified migration 001_create_users.rb looks like:

  1. class CreateUsers < ActiveRecord::Migration
  2.   def self.up
  3.     create_table :users do |t|
  4.       t.column :login_name,         :string, :limit => 20, :null => false
  5.       t.column :encrypted_password, :string, :limit => 40, :null => false
  6.       t.column :created_on,         :timestamp
  7.       t.column :updated_on,         :timestamp
  8.     end
  9.   end
  10.  
  11.   def self.down
  12.     drop_table :users
  13.   end
  14. end

Now it is a good time to improve our model, because we should not force user to enter his/her password in an encrypted form. ;)
The (shortened) content of the User model stored in the app/models/user.rb file (it was generated with the migration):

  1. class User < ActiveRecord::Base
  2.   validates_presence_of :login_name,
  3.                         :password,
  4.                         :password_confirmation
  5.   validates_uniqueness_of :login_name
  6.   validates_length_of :password,
  7.                       :minimum => 5,
  8.                       :message => N_("has to be at least %d characters long")
  9.  
  10.   attr_accessor :password_confirmation
  11.   validates_confirmation_of :password
  12.  
  13.   #for methods that generate a value of the encrypted_password field see the Depot application in the Agile Web Development with Rails book
  14. end

My goal is to show an example with more complicated model with artificial columns and to explain how to generate data for it.

Finally we will create a migration that inserts data to the User model:

script/generate migration add_user_ondrej

You should see the following output:

  1. exists  db/migrate
  2. create  db/migrate/002_add_user_ondrej.rb

The generated migration 002_add_user_ondrej.rb contains the default structure:

  1. class AddUserOndrej < ActiveRecord::Migration
  2.   def self.up
  3.   end
  4.  
  5.   def self.down
  6.   end
  7. end

To insert data we call the constructor of the User model in the up method. All fields listed as parameters of the validates_presence_of method have to be included in the constrcutor call. The down method has to delete inserted or all data.
The modified migration 002_add_user_ondrej.rb looks like:

  1. class AddUserOndrej < ActiveRecord::Migration
  2.   def self.up
  3.     User.create(
  4.       :login_name => "ondrej",
  5.       :password => "123456",
  6.       :password_confirmation => "123456")
  7.     end
  8.   end
  9.  
  10.   def self.down
  11.     User.delete_all
  12.   end
  13. end

That’s it! :)

Migrations are executed easily:

rake db:migrate

The output should look like:

  1. == CreateUsers: migrating =====================================================
  2. – create_table(:users)
  3. NOTICE:  CREATE TABLE will create implicit sequence "users_id_seq" for serial column "users.id"
  4. NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "users_pkey" for table "users"
  5.    -> 0.3231s
  6. == CreateUsers: migrated (0.3235s) ============================================
  7.  
  8. == AddUserOndrej: migrating ===================================================
  9. == AddUserOndrej: migrated (0.1505s) ==========================================

If you would like to change a migration (technically, if your application is already deployed, you should create a migration that alters the existing structure and/or data), you have to downgrade:

  • all migrations:
    rake db:migrate VERSION=0
  • the migration that enters data:
    rake db:migrate VERSION=1
  • in general:
    rake db:migrate VERSION=N
    where N is the number of the migration that will not be cancelled.

Conclusion
It was shown how to create a model with a migration and how to create an additional migration that generates data for the created model.

How to generate data for two tables with a join? The story will continue in two weeks… (I am going to Sweden for one week. :)

These icons link to social bookmarking sites where readers can share and discover new web pages.
  • del.icio.us
  • DZone
  • Digg
  • Reddit
  • Technorati
  • Furl
  • NewsVine
  • Slashdot
  • Ma.gnolia
  • StumbleUpon

2 Comments »

Comment by DJPaul

April 7, 2007 @ 11:40 am

Thanks for this info re: putting data into models via migrations, just what I needed.

Comment by ondrej

April 7, 2007 @ 12:10 pm

You are welcome :)

RSS feed for comments on this post. TrackBack URI

Leave a comment

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Comment Preview


commercial break :)

Make an account on slicehost.com -- a really good hosting where you have your own virtual machine. I installed Gentoo there = I like it very much ;)