foreign_key: belongs_to & has_one difference

1.6k views Asked by At

I don't understand difference of those 2 association types.

For example, we have 2 tables:

boxers

[ id, name, title ]

titles

[ id, name ]

So, we can specify our associations this way:

boxer.rb

belongs_to :title, forign_key: "title"

And then use it like Boxer.find(1).title.name

Logically, each boxer always has one record in titles and each title has many boxers.

So why wouldn't we specify has_one and has_many relations? And when i specify belongs_to :title, forign_key: "title", it means that FK point on table boxers in my case (boxer.rb). But when I try to write has_one :title, forign_key: "title", it search column title in titles table. Why?

3

There are 3 answers

0
zkcro On

I think the confusion may stem from the association between your models being somewhat counterintuitive. You're saying each boxer has one title, but each title may have multiple boxers, correct? In that case, while intuitively we'd talk about titles belonging to boxers, your association should actually be the other way around: each title has many boxers, and each boxer belongs to one title. So:

# title
has_many :boxers

# boxer
belongs_to :title

Where you have a has_one or has_many association in Rails, the other side must always have belongs_to, and this should be on the model that has the foreign_key to the other table. From the above, Rails would normally assume that the boxers table has a column called title_id, which would correspond to the values in the id column of titles. Given you've not done that, you'll need to provide the additional information so Rails can properly handle the association.

Specifically, you'll need to tell Rails that the Boxer model is using title as it's foreign key, and that this corresponds to the Title's name:

# title
has_many :boxers, primary_key: :name, foreign_key: :title

# boxer
belongs_to :title, foreign_key: :title

I'd recommend instead moving to a more standard setup, and dropping the title field on boxer in favour of a title_id. It's probably also worth having a look through the RailsGuide on Active Record Associations, as that has quite a bit of detail on exactly how all this works.

7
Arslan Ali On

Generically, if you do not mention any thing, and just write the following:

class Boxer < ActiveRecord::Base
  belongs_to :title
end

And then you do:

boxer = Boxer.find_by_id(1)
puts boxer.title 

boxer.title; it goes into titles table, and searchs there for a row where the id is same as the boxer_id stored in the object on which you are calling the method boxer.title.

That goes when you do not specify a foreign_key in the model.

But if you do specify a foreign_key like following:

class Boxer < ActiveRecord::Base
  belongs_to :title, foreign_key: 'title'
end

Now, in boxers table, there must be a column title that will store the id of of title it belongs to.

The difference is just: If you do not specify a foreign_key, it will go and look for title_id, but when you specify a foreign_key, it will search for that one instead.

0
Daiku On

You put the belongs_to on the model with the foreign key index into the other model. On the other model, you put the has_one. Both allow you to override the name of the fk column, but it's always in the table with thebelongs_to.

I use this trick to remember:

A dog belongs_to his owner (not the other way around), and the owner has_one dog. The dog wears the id tag with his owner's number, not the other way around.