A A

How To Use Fixtures to Populate Your Database in Rails

Fri, Jul 11, 2008

Code, Ruby on Rails, SQL

UPDATE: I’ve been using this method for awhile now: http://railspikes.com/2008/2/1/loading-seed-data

Seed data is data that the app is dependent on. It is data that has to exist if you were to wipe the database clean and reload your schema. Some examples would be a list of cities/states, a list of categories, or the initial ‘admin’ user account.

Most people looking at this thread want seed data rather than to populate their database with test/generated content. For the latter, you can go the route below or try Forgery

This is a response to the email I’ve been getting asking me how to use fixtures to load data into a database.

You want to create dummy entries in your Rails app, either for testing, for development, or for production, to make your site appear popular. Whatever the reason, populating your database can be done easily using fixtures.

While rake/fixtures/migrations can get a lot more complex, this will be a brief introductory example.

Initial App setup

$ rails characters
$ cd characters/

Edit config/database.yml – We only need a development database. So open up PHPMyAdmin or the MySQL command shell and:

mysql> CREATE DATABASE characters_development;
Query OK, 1 row affected (0.00 sec)

(I’m assuming you’re using MySQL. You can use anything; SQLite, Postgres, etc..)

Create a model and a table in the database (using a migration)

$ script/generate model Character
      exists  app/models/
      exists  test/unit/
      exists  test/fixtures/
      create  app/models/character.rb
      ....
 
$ vim db/migrate/001_create_characters.rb

Sexy migration:

class CreateCharacters < ActiveRecord::Migration
  def self.up
    create_table :characters do |t|
      t.string  :name, :alias, :motto
      t.timestamps
    end
  end
 
  def self.down
    drop_table :characters
  end
end

Now migrate development (default environment):

$ rake db:migrate

Create the characters fixture

$ vim test/fixtures/characters.yml


This is a YAML file. It is easy to read and easy to edit. (One may also use the CSV format, extension .csv).

Some notes:

  • Each model has a corresponding fixture file with the same name in the test/fixtures/ path in your app’s root directory.
  • Each ‘fixture’ is a record in the database table, and consists of a label, and then values for each field (i.e., name, age, etc..).
  • Optional fields can be skipped. They will default to either NULL, or their default value set in the database.
  • The id doesn’t need to be specified. It will be auto-incremented automatically, but be careful of conflicts if you’re specifying the ID for some fixtures and not others.
  • Whitespace matters. Make it purty.
  • You may embed Ruby code in the fixture file, in both the YAML and CSV fixture formats. (i.e., <% f() -%> or created_at: <%= Time.now %>)
  • To load fixtures, you use rake db:fixtures:load. This loads the fixtures into your current environment (can be set via RAILS_ENV shell var or in config/environment.rb).One thing that might be confusing is the fact that fixtures reside in test/. Many people believe that fixtures can only be used in the test environment, but this is not the case.
  • When a fixture are loaded, any data previously in the table will be wiped out. You don’t need to fear altering or losing your test data when testing (i.e., destroy methods, …), just reload the fixtures. In the test environment, fixtures may be loaded before each test case is run by using the fixtures method (fixtures :employees, :orders, …). Keep in mind that you’re using a freshly loaded fixtures between every case method (if you’re loading).

This is what our fixture file should look like. I will dissect it below:

riddler:
  name: Edward Nashton
  alias: Riddler
  motto: Riddle me this, riddle me that
 
pp:
  name: Peter Parker
  alias: Spiderman
  motto: One for JJ
  created_at: &lt;%= Time.now %&gt;
  updated_at: &lt;%= Time.now %&gt;
 
JILL:
  id: 101
  name: Jill Valentine
  motto: Im a member of S*T*A*R*S
 
dan_forden:
  name: Dan Forden
  motto: Toasty!
 
hotness:
  name: Sorceress
  motto: Time is mana
 
Airplane:
  name: Steve McCroskey
  motto: Looks like I picked the wrong week to quit sniffing glue.

Let’s examine the first fixture:

riddler:
  name: Edward Nashton
  alias: Riddler
  motto: Riddle me this, riddle me that
riddler:

This is a name for the fixture. It begins at the beginning of the line, and it doesn’t matter what you call it. That information is not saved in the database and you might never use it. Ideally, names should be descriptive. You may also just go generic, “person1, person2, person3″ etc.

When using the CSV format, fixture names are generated automatically and are in this format:

model_name-counter

In our example above, our fixtures would be named character-1, character-2, etc.

  name: Edward Nashton
  alias: Riddler
  motto: Riddle me this, riddle me that

These three are the values this fixture (record) will have in the database. They go under the fixture name, tabbed or spaced evenly. When going on to the next fixture, just leave a blank line.

The above is like doing this:

&gt;&gt; r = Character.new
&gt;&gt; r.name = 'Edward Nashton'
&gt;&gt; r.alias = 'Riddler'
&gt;&gt; r.motto = 'Riddle me this, riddle me that'
&gt;&gt; r.save

id, created_at and updated_at are optional, but may be specified. Keep in mind that this file is going to be preprocessed with Ruby, so you may embed Ruby code (ERb) anywhere in the file.

Load Fixture into Development Database

This loads fixtures into the current RAILS_ENV, which, by default, is development.

$ rake db:fixtures:load

If you check your database now, you will find the fixtures you just loaded residing in the specific table. Let’s poke at this table in the Rails console.

$ script/console
&gt;&gt; them = Character.find(:all)
=&gt; [#&gt; them[0].name
=&gt; Edward Nashton
 
&gt;&gt; them[0].alias
=&gt; Riddler
 
&gt;&gt; them[0].motto
=&gt; Riddle me this, riddle me that

See these links for more info:

External links


Add me. I'm lonely Why not subscribe to the feed?. If you’re on a mobile device I suggest Viigo

Sharing is Caring:
  • del.icio.us
  • Reddit
  • Facebook
  • Mixx
  • Digg
  • Google Bookmarks

Tags: , , , , , ,

5 Comments For This Post

  1. Jeff Says:

    Just fyi, in Rails 2.1 you can also skip the manual database creation step by doing this:

    rake db:create:all

    This will create all of your local databases if they don’t exist yet (i.e. only those databases in database.yml that use localhost or 127.0.0.1 as their host name).

  2. Jamie Says:

    So I’ve got production data that I need in every instance of my app. The best example is a “countries” table for user selection. My take on this is to put that type of data directly in a migration. That way it lives in every instance and gets migrated appropriately.

    My problem is that when I do a rake db:fixtures:load, it drops anything created in my migrations. :/ Am I doing it wrong?

    J

  3. Isam Says:

    Hey Jamie,

    rake db:fixtures:load is dropping the current records in your database (those created by the migrations) before adding its own data. This prevents duplicates from being added every time you run tests. You can keep the data in a fixture.

  4. Vincent Dogradi Says:

    Using fixtures to populate default or initial database values may cause some problem due to the way the id are generated. In fact, the id seems to be generated by calculating a hash of the fixture name.

    If you use this to populate a “mutable” table (like user table for instance), you will have your sequence of id beginning to a possible (random) high value. Say if you have 1 admin user loaded from fixture with id=283156471, then all users that will be added will have consecutive id beginning at 283156472.

  5. Jason Boxman Says:

    I have been using seed-fu plugin lately for handling data seeding and I’ve found it to be a nice solution. I was always uncomfortable with the idea of using my testing fixtures for seeding. The best part, besides being able to key off a field (or fields) and change existing seed data attributes in production, is that it’s all just Ruby inside, not YAML.

Leave a Reply