Seeding Initial Data to Rails Applications
Table of Contents

Background

Sometimes we need to create initial default data to a Rails application. Some people used to use migrations for this task. Now this is a very bad practice because it makes migrations very fragile. Also migrations aren't really designed to import data into the database, but to modify the database structure itself, and it should remain that way. Just think, what would happen if you create a migration for importing initial data to an application, if you know it might change afterwards? will you be willing to rollback all the changes just to change that migration? It sounds pretty dirty doesn't it?

So here are some examples of when we need to seed initial data to a rails application:

  • Some pre-defined categories for our blog
  • A super user that will take the role of god of the application
  • Some templates for my CMS
  • Roles for my users, because no role, no user :D

Solution

A best practice pattern is to include the code that should handle this "one-time" tasks in the db/seeds.rb file:

touch db/seeds.rb

Then edit the file and include code that you'll be sure it won't break stuff even after running it a couple of times:

super_user = User.find_or_create_by_email "admin@superblog.com", :encrypted_password => super_password, :salt => super_salt
super_user.admin = true
super_user.save(false) # avoid validations

I use "find_or_create" finders in order to avoid having multiple admins if I ran this a couple of times.

A seed file can also be used for more geeky stuff, such as asking for a customized admin email and password on install:

require 'rubygems'
require 'highline/import'
email = ask("Enter the admin email:  ") do |q|
  q.echo = true
  q.validate = /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
end

password = ask("Enter the admin password  ") { |q| q.echo = '*' }
user_role = UserRole.find_or_create_by_title(:title => 'Admin')
user_role.super_admin = true
user_role.save
user = User.find_or_create_by_email(:email => email)
user.password = password
user.user_role = user_role
user.save(false)
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License