Using Rails Fixtures to Seed a Database
It’s no mystery that I’ve grown to love Rails fixtures. And I tend to mostly use relational databases in my applications, specifically PostgreSQL.
Most applications have ancillary data that’s required to support the main function of the application — think drop-downs with states for shipping or credit card type.
This data is almost always never interesting, but completely necessary for the application to work as expected. So when it comes to time send your little baby to production, only to find your users can’t pay because they can’t pick their credit card type, your world comes crashing down.
If you have those credit card types in fixtures from the start, loading them in to your development of production database is just a rake
task away.
The Problem
Let’s assume our application requires us have a list of supported credit card types, and the user is required to pick from the list to pay for the awesome stuff we sell. A sample fixture might look like:
This is a somewhat trivial example because the name
matches what one might expect in a potential transaction record if we had a credit_card_type
field or something similar if we denormalized.
Perhaps we have a field credit_card_type_id
in a transactions
table that references the foreign key of the related CreditCardType
record.
So how do we get these records in to our development and production databases?
The Solution
Fortunately, Rails has our backs. The following rake test is available from a default Rails application:
The db:fixtures:load
task is an interesting start, but quickly we realize it might be a little heavy-handed. If this application has users, we probably wouldn’t want to copy them to production. They might, however, be a great starting pointing for development.
So how do we handle getting trivial model data in to production for only specific models?
It turns out that we can specify ONLY the models we want to load by using the FIXTURES
environment variable:
Note: The name of the fixture file (usually the database table name) should be used as the value for FIXTURES
, not the model name.
With that single command, any environment we specify will immediately get the data for our 3 credit card types.
A word of warning, if we run this command multiple times, it will seed the table multiple times. It’s not idempotent.
Additionally, if we wanted to load more than just a single fixture, we can specify the names of the files separated by commas:
Let’s take a quick look at how Rails implements this functionality, specifically the determination of single models:
If the FIXTURES
variable is present, code teases appart the model names and looks in the fixtures directory and loads the YAML fixture file for that table name.
An interesting side note, it’s possible to specify alternate directories for fixture using the FIXTURES_DIR
variable. I personally haven taken advantage of this, but could be useful if you want to keep specific fixture files for production that might be different than those that reside in test/fixtures/*
.
I wouldn’t suggesting using this approach for anything that needs to reference other foreign keys. When you’re transferring to a different database, foreign keys will not match and your application will likely not work as expected.
Summary
This approach has saved me quite a bit of time in my last few applications. Build it once, use it everywhere. As mentioned above, using this approach to seed database records with a foreign key should be avoided.
Most applications have a number of tasks needed for a developer to get up and running. Combining fixture data with additional seed data placed in db/seeds.rb
can give you the best of both worlds, while still ensuring you have robust data to test against.