<

ActiveRecord's confusing Single Table Inheritance behavior with NULL type

Published on
3,319 Points
319 Views
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
  ...
end

class AdminUser < User
  ...
end

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';
...[ok]
mysql> exit
Bye
$> bundle exec rake db:test:prepare # <-- My Mistake
...[ok]

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.
0
Comment
Author:mltsy
0 Comments

Featured Post

Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Join & Write a Comment

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…
Look below the covers at a subform control , and the form that is inside it. Explore properties and see how easy it is to aggregate, get statistics, and synchronize results for your data. A Microsoft Access subform is used to show relevant calcul…
Suggested Courses
Course of the Month14 days, left to enroll

Keep in touch with Experts Exchange

Tech news and trends delivered to your inbox every month