A not so funny orphaning issue or Rails's has_one relationships

If you are using ActiveRecord and keeping your models pretty thin and properly normalized, you will surely reach for the very nice has_one relationship. It’s basically a one to many relationship, but Rails thinks for you and keeps only one child model available for use. It’s pretty easy to set up, given that you have two models, let’s say Post and Post::Metadata

class Post < ActiveRecord::Base
 has_one :post_metadata
end
class Post::Metadata
 belongs_to :post
end

OK, this part is pretty straightforward, you create your records either by using Post::Metadata.create(post_id: 42, other_attrs) or by using Post.find(42).create_post_metadata(attrs). If there already is a has_one child record, and you create a new one, the old one will be orphaned, which is a default option, and you should be thinking of it before you start messing around. This can also be the desired option, maybe you are storing some photo urls that aren’t being used anymore, and have a callback on them to destroy the record after you remove the file from the third-party storage. There is one tiny kink that cost me a few hours of life today, although you create a child record in a has_one relationship, the mere action of building an association (just instantiating it, not creating) is enough to orphan the previous record, without it rolling back if the newly built record is destroyed.

Leave a Reply

Your email address will not be published. Required fields are marked *