Using Migrations for Generating Initial or Test Data
The 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:
-
exists app/models/
-
exists test/unit/
-
exists test/fixtures/
-
create app/models/user.rb
-
create test/unit/user_test.rb
-
create test/fixtures/users.yml
-
exists db/migrate
-
create db/migrate/001_create_users.rb
The generated migration 001_create_users.rb contains the default structure:
-
class CreateUsers < ActiveRecord::Migration
-
def self.up
-
end
-
-
def self.down
-
end
-
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:
-
class CreateUsers < ActiveRecord::Migration
-
def self.up
-
create_table :users do |t|
-
t.column :login_name, :string, :limit => 20, :null => false
-
t.column :encrypted_password, :string, :limit => 40, :null => false
-
t.column :created_on, :timestamp
-
t.column :updated_on, :timestamp
-
end
-
end
-
-
def self.down
-
drop_table :users
-
end
-
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):
-
class User < ActiveRecord::Base
-
validates_presence_of :login_name,
-
:password,
-
:password_confirmation
-
validates_uniqueness_of :login_name
-
validates_length_of :password,
-
:minimum => 5,
-
:message => N_("has to be at least %d characters long")
-
-
attr_accessor :password_confirmation
-
validates_confirmation_of :password
-
-
#for methods that generate a value of the encrypted_password field see the Depot application in the Agile Web Development with Rails book
-
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:
-
exists db/migrate
-
create db/migrate/002_add_user_ondrej.rb
The generated migration 002_add_user_ondrej.rb contains the default structure:
-
class AddUserOndrej < ActiveRecord::Migration
-
def self.up
-
end
-
-
def self.down
-
end
-
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:
-
class AddUserOndrej < ActiveRecord::Migration
-
def self.up
-
User.create(
-
:login_name => "ondrej",
-
:password => "123456",
-
:password_confirmation => "123456")
-
end
-
end
-
-
def self.down
-
User.delete_all
-
end
-
end
That’s it! :)
Migrations are executed easily:
rake db:migrate
The output should look like:
-
== CreateUsers: migrating =====================================================
-
– create_table(:users)
-
NOTICE: CREATE TABLE will create implicit sequence "users_id_seq" for serial column "users.id"
-
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "users_pkey" for table "users"
-
-> 0.3231s
-
== CreateUsers: migrated (0.3235s) ============================================
-
-
== AddUserOndrej: migrating ===================================================
-
== 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. :)










