Welcome to Giant Robots Smashing Into Other Giant Robots — a weblog about development, business, design and technology — written by thoughtbot.
Nulls. Can't live with 'em can't live without 'em
Let’s start with a simple example:
1 2 3 4 5 6 7 8 9 10 11 |
class Company < ActiveRecord::Base has_many :users end class User < ActiveRecord::Base belongs_to :company end |
A Company has many users and a User belongs to a Company. A 1-to-many from Company to User.
However, for this app a User may or may not belong to a Company. In other words a Company is optional for a given User.
Let’s look at our database.
companies (id, name)
users (id, email, password, company_id)
Obviously the users table has a foreign key linking to the companies table. But for Users that don’t have a Company the value for that field will be NULL. To me that seems strange. I don’t like the fact that there exists a row in a table that has a relationship to another table with no value for that relationship. How would it have been created in the first place. Its like having a Comment without a Post, you’d never have a Comment in your database with a NULL value in its post_id field.
One argument is that the value should be NULL because in the object world a User object’s value for its Company would be nil. The database equivalent of nil would be NULL. But let’s try something else out to see where it takes us.
I want to get rid of that company_id foreign key in the users table. I think we’re missing a concept here.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class Employment < ActiveRecord::Base belongs_to :user belongs_to :company end class User < ActiveRecord::Base has_one :employment end class Company < ActiveRecord::Base has_many :employments has_many :users, :through => :employments end |
There it is Employment. Employment looks a little odd because its a join model but one side of it is a 1-to-1. A User has one Employment and a Company has many Employments and has many Users :through Employments. Its fine because in the database world a 1-to-1 looks exactly like a 1-to-many.
Now to get a User’s Company you’d go
user.employment.company |
Maybe I can use has_many :through, to make it feel just like the original design without Employment.
1 2 3 4 5 6 7 |
class User < ActiveRecord::Base has_one :employment has_one :company, :through => :employment end |
Nope. Rails doesn’t like it. That sucks.
How about performance?
In the first design without the Employment model,
user.company |
would result in 1 join, from the users table to the companies table.
In the second design with the Employment model,
user.employment.company |
would result in 2 joins, 1 from the users table to the employments table and 1 from the employments table to the companies table. If you have a web page that lists all your Users and all their Companyies or vice versa then this could get expensive. In that case it would be best to use ActiveRecord::Base#find’s :include parameter to eagerly fetch the other side of the relationship.
Damn NULLs.
About this entry
You're reading an entry on GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS, the company weblog of thoughtbot, inc.
- Author:
- Jared Carroll
- Published:
- January 18th 09:04 AM
- Updated:
- September 30th 09:57 AM
- Sections:
- Development
thoughtbot is hiring
We are hiring web developers and web designers in both Boston and New York, NY.
What are we up to?
We built Shoulda, an eclectic set of additions to Test::Unit; Paperclip to manage uploaded files without hassle; Jester, a REST/ActiveResource client library written in Javascript, and Squirrel, an enhancement for ActiveRecord's find syntax; — amongst some other projects.

Chad (President) and Jon (CTO) co-authored a technical book titled Pro Active Record: Databases with Ruby and Rails, which explores the ins and outs of the ActiveRecord ruby library. You can buy it today at Amazon.com.
About thoughtbot, inc.
We are a small web application development consulting business, with offices in Boston, MA and New York, NY. If you're looking to find a team for your next web development project or your new web application — get in touch.
9 comments
Jump to comment form