“Belongs To” and “Has One” – aren’t they the same thing?

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…

Do you know what to do but not how it works?

Ever wanted to understand why Rails views work the way that they do? Why variables from your controllers are visible inside your views?

Sign up below to get a free 5 part email course on how Ruby on Rails view rendering works and gain a deep understanding of the Rails magic.

We will send you the course, plus the occasional update from this web-site. But no spam, we promise, and it's easy to unsubscribe

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:

people:

id name
1 Emma
2 Bob

phone_numbers:

id phone_number person_id
1 123456 1
2 987654 1

So here, we have two people, but one of them, Emma, has two phone numbers. And we would represent this in Rails like so:

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:

projects:

id name
1 Project X
2 Project Y

reports:

id complicated_value_1 complicated_value_2 project_id
1 999.99 108383 1
2 74742.22 98373 2

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:

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).

Do you know what to do but not how it works?

Ever wanted to understand why Rails views work the way that they do? Why variables from your controllers are visible inside your views?

Sign up below to get a free 5 part email course on how Ruby on Rails view rendering works and gain a deep understanding of the Rails magic.

We will send you the course, plus the occasional update from this web-site. But no spam, we promise, and it's easy to unsubscribe