ActiveRecord's confusing Single Table Inheritance behavior with NULL type

Published on
3,298 Points
Last Modified:
Recently I spent hours debugging an issue in a Rails project where ActiveRecord was causing MySQL errors trying to create a User object of a class at the top level of a Single Table Inheritance model structure.  It turns out `.create` behaves differently based on the default value of the type field!
I recently had the pleasure of participating in porting a very large and complex Rails project from DataMapper to ActiveRecord. Among the models that had to be ported was a set of User models that used Single Table Inheritance (STI) to distinguish one type from another. Here is a simplified version of what that looks like:
class User < ActiveRecord::Base

class AdminUser < User

Open in new window

In this case, the same table is used to store data about both Users and AdminUsers. Generally, the 'type' field is used to tell the difference between Users and AdminUsers, by storing the class name of the object being saved (i.e. 'AdminUser'). This all works out fine 99% of the time. When you create a new model that uses STI, ActiveRecord presumably takes care of setting up a default value for the column and marking it as non nullable, etc.

I say presumably, because I haven't actually done it - my database was already setup by DataMapper years prior. And this is what happens when you try to create a Single Table Inheritance model (User.create) on a table that doesn't have a default value for the 'type' column:
ActiveRecord::StatementInvalid: Mysql::Error: Column 'type' cannot be null

Open in new window

ActiveRecord tries to insert a new row, setting the type column to NULL, which is a horrible way to store data for an STI model! Fortunately, in my case, type is a non-nullable column, so I get an error.

My real confusion came when I updated my dev database to have a default for the type column (because somehow the production database already had a default, so clearly my dev database was just out of sync):
mysql> ALTER TABLE users MODIFY COLUMN type varchar(50) NOT NULL DEFAULT 'User';
mysql> exit
$> bundle exec rake db:test:prepare # <-- My Mistake

Open in new window

I thought this was all I had to do, but it turns out running db:test:prepare just matches your test database to your schema.rb file, and my schema.rb file hadn't been updated, so now User.create is working fine in development, but broken in testing, and THAT kind of failure is the most fun you can possibly have trying to debug a test: the kind that only happens in the test environment! :D

Eventually, I came to understand all of the above, in addition to the fact that I needed to run db:migrate in order to update my schema.rb file BEFORE running db:test:prepare. Once I did that: voila! User.create actually uses the default value of the type column to insert new User objects.

Moral of the story:
  • NEVER let your development database get out of sync with production. If it is: blow it away with a db:schema:load and start over with new dev data! (Or get a production dump, or something)
  • Choose your ORM wisely. R.I.P. DataMapper - I'll miss your elegant abstractions... but not your bugs.
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions

Featured Post


Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

Join & Write a Comment

Suggested Articles

The purpose of this video is to demonstrate how to set up the WordPress backend so that each page automatically generates a Mailchimp signup form in the sidebar. This will be demonstrated using a Windows 8 PC. Tools Used are Photoshop, Awesome…
Visualize your data even better in Access queries. Given a date and a value, this lesson shows how to compare that value with the previous value, calculate the difference, and display a circle if the value is the same, an up triangle if it increased…
Suggested Courses

Keep in touch with Experts Exchange

Tech news and trends delivered to your inbox every month