The Dependent Option for Model Associations in Rails
A simple way to level up your Ruby on Rails apps.
Recently when developing a Rails application, I ran into a snag when deleting an instance of a parent model (e.g. Artist
). Its related records (or children; e.g. Song
s) stuck around. This caused several bugs wherever those related records (Song
s) were called because its foreign key (artist_id
) tried to load an associated artist
that no longer existed. This forced me to manually delete the broken records in Rails console and implement a method to delete each associated record upon deletion of a parent object ( it reeked of code smell). But there’s a better way…
Enter model association dependencies.
The has_many
, belongs_to
, and has_one
relationships support the :dependent
option (which is added to an association like :validate
or :through
would be). This allows the developer to specify how associated records should be deleted when the owner is deleted.
Let’s hit the ground running and start with an example:
An Artist
has_many
songs and a Song
belongs_to
an Artist
. Let’s consider this:
johnny_cash = Artist.create(name: "Johnny Cash")ring_of_fire = Song.create(title: "Ring of Fire", artist: johnny_cash)
However, if johnny_cash
is destroy
ed, that leaves ring_of_fire
with an artist_id
pointing to an Artist
that no longer exists, stuck in limbo! So calling something like Arist.find(ring_of_fire.artist.id)
will return:
ActiveRecord::RecordNotFound (couldn't find Artist with 'id'=2)
This will be an issue for any method that calls on aSong
's Artist
. You may want to also delete any songs associated with an Artist
when you delete it. There is a very eloquent solution to this and where the:dependent
option comes into play:
Now, when johnny_cash
is destroy
ed, its associated songs
are also destroy
ed:
destroy
is a great start, but Rails includes a few options for dependent
that allows us to customize our apps even further. The options for dependent
include:
:destroy
causes all the associated objects to also be destroyed:delete
or:delete_all
(:delete_all
is used withhas_many
relationships) causes all the associated objects to be deleted directly from the database (so callbacks will not execute):nullify
causes the foreign key to be set toNULL
. Polymorphic type column is also nullified on polymorphic associations. Callbacks are not executed. This is useful when you don’t want to delete or destroy an associated object, but clear the foreign key of its owner
:restrict_with_exception
causes anActiveRecord::DeleteRestrictionError
exception to be raised if there are any associated records
:restrict_with_error
causes an error to be added to the owner if there are any associated objects
Conclusion
The dependent
option is a handy tool that can help in a variety of different ways (with its different options) that makes life as a Rails developer that much sweeter. Deleting an object can leave its related records broken with foreign keys that point to instances that no longer exist. The dependent option for Rails associations allows related instances to be destroyed or deleted, foreign keys nullified, and can even prevent objects from being deleted if there are associated objects. Give the dependent option a shot in your next project, you won’t be disappointed!
This post is written by a Power Code Academy Student Developer.