Rails associations are a great way of mapping relational databases to objects. They add an easy-to-use abstraction over the top of relational concepts like many-to-many relations and spare you some of the details of building joins.
But every now and then the abstraction can leave you confused. Nowhere more than in the difference between the “has_one” association and the “belongs_to” association. If you’re having trouble understanding the difference then read on…
We already touched on the “belongs_to” association when looking at “has_many“s. If a Person has many PhoneNumbers then each PhoneNumber belongs to a Person. This is reflected in the database as so:
So here, we have two people, but one of them, Emma, has two phone numbers. And we would represent this in Rails like so:
class Person < ActiveRecord::Base
has_many :phone_numbers, dependent: :destroy
class PhoneNumber < ActiveRecord::Base
Hopefully that all makes sense – the belongs_to denotes that the PhoneNumber has a person_id that references a Person.
The other day, the project I was working on had the following tables:
So if you look at the structure alone, you’d say this is pretty much the same situation as people and phone-numbers – there are projects and each project can have many reports.
Apart from the requirements of the application demanded that each project can only have one report.
In this case I chose to represent the models as so:
class Project < ActiveRecord::Base
has_one :report, dependent: :destroy
class Report < ActiveRecord::Base
One of the weaknesses of relational databases is that the relations and tables only describe the structure of your data. They do not describe your application.
And as I said, each project only linked to a single report.
So why not represent things the other way round – with project having a report_id column and a belongs_to association?
Well, the point of the application was to represent projects; the reports are just secondary. The project is more important than the report.
In the original example, if I delete the person, then that person’s phone-numbers are no longer relevant, so I want them deleting as well.
In database terms, this is called a master-detail relationship; the master is the person, the detail the phone_number.
And in Rails, this is represented by adding dependent: :destroy to the association definition.
In the project/reports example, if I delete the report then the project is still valid. So we cannot make the report the master of the project. If I delete the project then the report becomes invalid and we want it deleting.
So the project has to be the master of the report. It just happens to be a master with a single detail record, not with multiple detail records.
All of which means that when you’re looking at the associations between your objects you have to decide “who is more important here?”
“If I delete this record then who is affected?”
“Does it make sense for this record to exist without this other record?”.
Once you have an understanding of the dynamics of your models you can then decide which is subordinate to the other (using belongs_to) and which is in charge (using has_xxx dependent: :destroy).