Updated 4/24/09: If you want to use foreign keys in your Rails project, checkout the migration_helpers and sexy_migrations plugins for a couple of options.
Jeff from Softies on Rails recommends doing without foreign keys and posted You Don't Need Foreign Key Constraints In Rails to ask for our opinions.
He's right. You don't need foreign key constraints, just like you don't need a seatbelt in your car. Your car will drive perfectly fine without one. You can drive cross-country at 120 miles per hour without a seatbelt, but may the Flying Spaghetti Monster have mercy on your soul you if you hit a tree.
It looks like everyone is on the FK side so far in the discussion. Of course, DHH hasn't commented yet, so that may change. Rails is notorious for being "opinionated software", and this is one of those opinions that a lot of people happen to disagree with, and is one of very few (the only?) cases where the majority of Rails users overwhelmingly, strongly disagree with the Rails philosophy.
One argument against FKs is that Rails' ActiveRecord provides nifty relationship wiring and validation utilities, like belongs_to, has_many, and has_and_belongs_to_many. Since this logic is defined in the model, repeating it in the database is not DRY. Plus, FKs make it harder to deal with complicated relationships. You can't delete an entity without first deleting its child entities (which :dependent => :destroy will do for you if you want closure).
These aren't bad reasons, but they're not good enough to outweigh the benefits of foreign keys, or to offset the potential cost of a weird bug that creates inconsistent data. A fairly common problem in Rails with foreign keys is setting up and tearing down test fixtures -- but let's face it, fixtures pretty much suck, and there are ways to deal with the problem. Referential integrity is more important than cheap, lazy fixtures.
Foreign keys may repeat something that's already defined in the model code, but so what? Relationships rarely change, and when they do, you're probably going to be changing the database anyway. It's not as if they're likely to get out of sync.
Foreign keys are pretty easy to create, especially if you use a plugin like sexy migrations, and they don't add much overhead. (Oddly, Rails 2.0 borrowed quite a bit from sexy migrations but didn't include the foreign key stuff.)
Some databases provide the option to automatically index the columns that are foreign keys, saving you the trouble of adding the indexes yourself. (Foreign keys tend to be columns that you'd want to index. Consider fetching a post's comments: SELECT * FROM comments WHERE post_id = 1 should use an index on post_id.)
And of course, without foreign keys how can you generate an ER diagram to hang on the wall for everyone to admire? (Note: I don't do that.)
Wednesday, January 09, 2008
You Should Use Foreign Key Constraints in Rails
Labels: database, foreign keys, rails
Subscribe to:
Post Comments (Atom)


11 comments:
We had the same argument here at work. We decided to go down the not using foreign key path in hind sight it's caused us problems because of misteps in the rails code that didn't enforce the referential integrity like we would have liked.
What do you think about uniqueness constraints? E.g. a has_one should REALLY only allow a row-to-row relationship between tables. This isn't the case in rails and I have to put a validates_uniqueness_of in order to do this--is there a better way?
Hi Corbin, thanks for the comment.
I think uniqueness constraints should also be used where they make sense. validates_uniqueness_of can help, but isn't perfect because race conditions can occur when you have more than one Rails instance (such as in a pack of Mongrels).
I don't have any hard numbers to support this, but it seems that many (definitely not all) unique constraints are on columns that should be indexed, so you can make it a unique index.
Likewise, you can use CHECK constraints to back up validates_include_of and boundary validations, and NOT NULL to back up validates_presence_of (unless you're using single-table inheritance).
Modern databases automatically index the columns that are foreign keys, saving you the trouble of adding the indexes yourself.
This may apply to some db's, but isn't common afaik.
Indexes on a foreign key don't always make sense, e.g. if they refer to a write-only table.
@Anonymous: You're right, SQL Server and Oracle don't appear to do it. But MySQL and Postgres do, and they're the most common database systems used with Rails. SQLite doesn't seem to use FKs at all, it's the new Rails default.
Indexes on FKs may not always make sense, but they do more often than not in the databases I've worked with. I'm not sure what the use cases are for a write-only table -- if you're never going to read from it, why both writing to it?
I have to agree with Jeremy here. FKs and uniqueness constraints need to be enforced in the database. Applications come and go (along with their bugs), but data lives forever. And once your database is out there, another team may end up writing to your database and that team may not be aware of the logical constraints in the data model.
I've seen the results of a team that decided against using FK constraints in a database (not using Rails, however). Bugs in the application resulted in garbage in the database that wasn't discovered until a couple years and terabytes later. One of my colleagues had the joy of spending 6 months as "data garbageman" cleaning up the database. Trust me, you don't want to go there. Ever.
Constraints are good. With Rails I've been using the redhillonrails_core plugin, but only :restrict... any :cascade or :set_null behavior I perform in Rails. Mainly I have the constraints there to force me to develop proper interfaces to handle deletes, knowing that until then my database integrity won't be compromised.
This is rather simple. If you want your business stay for long time -- data (database) OUTLIVE implementation of model and front-end. Today is RoR, tomorrow assembler_on_steroids or else... Do not bother with FK, normalization, good db practice if you build garage-based one-day apps.
I think what you are arguing over is the basic problem with ORM vs. a true object oriented database. Entities aren't truly objects and not all objects can be modeled as entities.
I read somewhere that ActiveRecord solved the problem by siding with the RDBMS side of the argument. It appears that is not the case for Foreign Key constraints.
so is there a plugin that will analyze your models and "duplicate" constraints in the DB for you [like validates uniqueness of, etc.]?
-r
@Roger - Not that I'm aware of. The closest thing is probably auto migrations, but I haven't used it and not sure if it knows about constraints.
http://github.com/pjhyett/auto_migrations/tree/master
Have a look at http://github.com/matthuhiggins/foreigner/tree/master. It's migrations helpers only, but does not fight Rails in any way.
Post a Comment