Simplify the delete operation in you Rails app
In the Ruby on Rails application I am working on, I have a model 'Album' and a model 'Photo'. As you have guessed, each album can have many photos. This is a typical one-to-many relationship and can be coded like this:
-
class Album <ActiveRecord::Base
-
has_many :photos
-
end
-
-
class Photo <ActiveRecord::Base
-
belongs_to :album
-
end
Since I want to have 'delete album' function in my application, I coded a destroy method in the my album controller.
-
class AlbumsController <ApplicationController
-
def destroy
-
@album = @account.albums.find(params[:id])
-
@album.photos.destroy_all
-
@album.destroy
-
end
-
end
Nothing fancy here. A simple "delete the photos first before you delete the album" operation. This code has been in my application for several months and I even used the pattern in my other controllers. A while ago I was working on the admin pages and I was reviewing another 'destroy' method when I thought why should my "destroy" method explicity destroy the child records in my controller. My controller should know as little as possible (or none at all) how my models are related. My initial idea is to let the 'Album' model handle the deletion of the 'Photo' records. I thought a 'before_destroy' callback would do the trick. Just to be sure there are no gotchas, I looked it up in the Ruby on Rails API. I just found out, there is a simpler way of "delete the photos first before you delete the album". In my 'Album' model, I included the 'dependent' option in the 'has_many' association.
-
class Album <ActiveRecord::Base
-
has_many :photos, :dependent => :destroy
-
end
All I need to do now is simply call '@album.destroy' in my controller and all child photos will be deleted. Sweet.
Aside from the 'dependent' option, there is also the 'exclusively_dependent' which can delete the child records in a single SQL statement (which is faster than traversing each child record) on the condition that the child table has no hook methods on deletion and it is used only by the parent table.
Did you enjoy this post? Why not leave a comment below and continue the conversation, or subscribe to my feed and get articles like this delivered automatically each day to your feed reader. If you don't have a feed reader, you can always have these articles delivered to your email inbox every day. Click here to sign up.



Great tip. I wasn’t aware of that option. Thanks.
:exclusively_dependent has been deprecated. Try :dependent => :delete_all instead.
Hmmm, so what would you do if you allowed a photo to belong to many albums, ala iPhoto?
How would you change things so that the photo could be deleted from an album, but still remain in other albums?
And how would you change it so that if the photo is deleted, it’s deleted from all albums?
Carmen, no problem. Thanks for dropping by
Evan, as always, you’re the Rails hacker.
Ian, thanks for these interesting questions. On question #1, initially I thought this would call for ‘habtm’. But on question #2 and #3, I have no idea if ‘habtm’ options can handle it. What would happen if an albums is deleted (in #2)? Photos that belong exclusively to the album will also be deleted? Or is the photo treated as a shared resource that if the numner of albums that reference it becomes zero, it can now be deleted? What would I do here is drop the ‘join’ and have a new model to represent this kind of relationship.
Usability wise, I still find it complicated to have photos belonging to many albums. This behavior of an “album” and “photo” doesn’t map smoothly to the real world (at least in my world :)). If I would want to have photos belong to many albums, I would drop the term “album” and instead use “labels” or “tags”.
This was discussed in the book Agile Web Development with Rails I think. Another reason why it’s considered the de facto Rails bible for RoR enthusiasts.