Mohit Jain
Mohit Jain

Reputation: 43949

What's the use of has_one :through here (example from Rails guide)

Example screenshot
(source: rubyonrails.org)

Why they are using has_one:through here. We can do the same with has_one only. What's the need of making a new class. Can you give me any good example?

Here is the link for the original example from Rails guide

EDIT

We can do the same thing in this way what's the use of making it a has_one:through

  class Supplier < ActiveRecord::Base
     has_one :account  
  end

  class Account < ActiveRecord::Base 
    belongs_to :supplier    // add a another column credit_rating in accounts table
  end

Upvotes: 4

Views: 2281

Answers (4)

John Topley
John Topley

Reputation: 115372

The has_one :through association is being used because the join model (AccountHistory) contains additional information related to the association between Supplier and Account i.e. a credit rating.

This example in the Rails guide is quite poorly chosen because it doesn't make it obvious why using has_one :through is better than using has_one with a credit_rating attribute on the Account model.

You should use the has_one :through or has_many :through associations when you need extra attributes on the join model that logically don't belong on the other models that form the association. A classic example is modelling the lending of books to users within a library. The date and duration of the book loan belong in the (loan) join model because logically they're not attributes that belong to the user or the book. In this case the use of a has_many :through association would be appropriate.

Upvotes: 2

nathanvda
nathanvda

Reputation: 50057

I think in this case it depends how you design your databasemodel. The advantage of Rails is, should you work with some legacy database, it can cope with almost all relation types. And in this case you would use the :has_one :through, because your database tables are modeled in such a way.

The advantages of using three models as proposed: there is a clean seperation between the Account and the AccountHistory. While you could model Account and AccountHistory as one combined model, there is no need. This might not seem as useful in such a small example, but for instance:

  • suppose AccountHistory has a lot of logging attached to it, to be able to calculate whether some account is still trust-worthy (or whatever); in this setup both models don't have to be loaded at the same time: you can now only use Account or AccountHistory
  • code pertaining to either models (History or not) are now seperated, making the intentions of your models cleaner

Upvotes: 1

Tomas Markauskas
Tomas Markauskas

Reputation: 11596

There are cases where you might not want to add a specific field to a table. In your example you really only need one table as you can just add account_number and credit_ranking to the suppliers table. But sometimes it's a good idea to store the data across several tables. Then you have to use the has_one (one-to-one) relationship.

In your example you could also just add the attribute supplier_id to account_histories and replace has_one :account_history, :through account with just has_one :account_history but that would be redundant and also complicate your code as you would need to make sure that you don't change one attribute and forget to update the other one.

UPDATE:

If you don't add the supplier_id attribute to account_histories then Rails won't be able to determine which row from that table belongs to which supplier. The only way to find that out is to look in the related accounts table. Without accounts you can't determine which account_history belongs to a supplier as the accounts_histories table doesn't have any foreign keys for the suppliers table.

To get the account_history for a supplier without the :through option you would have to do this:

Supplier.find(id).account.account_history

:through allows you to replace it with this:

Supplier.find(id).account_history

As you've written in your update you could add the credit_ranking attribute to accounts and have only two tables. That would be even more simple but you just might want not to store that attribute in the same table (because maybe you already have lots of other attributes and don't want to add even more of them).

Upvotes: 1

Joshua Partogi
Joshua Partogi

Reputation: 16425

Because by using has_one :through you can add more attributes to Account

Upvotes: 1

Related Questions