Two months ago today I posted about a bug in Active Record. A bug that reminded me to remain cautious about how much trust I put into a database abstraction layer. I am happy to now say that this particular bug has been fixed, and I got to help! In the process, I also got to add some new features. (see my original rant, Active Record, I love U but I still trust my database server a tiny bit more.
I discovered this bug when I was working on a chapter in my book on Active Record. I’m known to gladly take advantage of database constraints and triggers, and it was when I decided to test my code without these constraints, I discovered the bug. “Hey, Active Record isn’t doing what it’s supposed to!” I’ve since had a number of people ask me if what a more pragmatic way to work around this issue is, rather than go my route by adding a constraint/trigger.
You can now DO AWAY with code>ON DELETE CASCADE! (sort of)
The new release of Rails 1.0 Release Candidate includes some new options
for the has_many and has_one declarations.
Previously, you could do the following:
class Customer < ActiveRecord::Base
has_many :orders, :dependent => true
end
This was supposed to nullify the dependent records, but it didn’t!
My patch not only fixes this, but also gives more control with what
:dependent does. Now, you can pass the :dependent option to the
has_many and has_one declarations with either :nullify or
:destroy. This has a similar affect as ON DELETE CASCADE in those
fancy RDBMs like PostgreSQL.
Let’s take a closer look at these new options:
has_one
A spider has_one web, and the web belongs_to one spider. If you
destroy the spider, you would most likely want to destroy the web as
well.
class Spider < ActiveRecord::Base
has_one :web, :dependent => :destroy
end
On the other hand, in the case of a snail that has_one shell (and the
one shell belongs_to the snail), if you destroy the snail, you may
want to keep the shell. Remember, your crazy Aunt Ruby collects snail
shells.
class Snail < ActiveRecord::Base
has_one :shell, :dependent => :nullify
end
Now, for every snail we destroy, the shells, though once dependent on the snail, are now available for Aunt Ruby.
has_many
The same rules apply to the has_many association. Most people assume
that if you destory a beehive that you would destroy all the bees. I’d
like to think that they wander around until they find a new beehive to
join. So, we can :nullify their relationship when the beehive is
destroyed, thus making them homeless, but available for future hives.
class Beehive < ActiveRecord::Base
has_many :bees, :dependent => :nullify
end
Sadly, some people might want to destroy the bees along with the
destruction of the beehive. So, those people can pass the :dependent
option, :destroy.
class Beehive < ActiveRecord::Base
has_many :bees, :dependent => :destroy
end
Don’t worry, your usage of :dependent => true will now work, even
though it wasn’t working before.
I’m going to try to put more trust into Active Record now, and I hope that this new addition to the library finds itself useful for you. :-)
Take a peak at the cool new features in the latest version of Rails and see the Active Record CHANGELOG for more information.
Enjoy!