views:

120

answers:

4

Example screenshot

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
+1  A: 

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

jpartogi
@jpartogi Please check the edit part
piemesons
+1  A: 

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.

John Topley
@John Topley Please check the edit part
piemesons
@piemesons I've expanded my answer.
John Topley
@John thanks i got the point of has_many:through but i am not getting any good example where i can implement has_one:through. Please check my comment on Thomas post.
piemesons
+1  A: 

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

Tomas Markauskas
@Tomas. hey thanks for your reply. actually i want to know when to use has_one:through. The example mentioned in the question is from rails guide and i am not able to understand the usage of this has_one:through. When and where to use it. Does the example mentioned needs this has_one:through relationship.
piemesons
@piemesons I've updated my answer.
Tomas Markauskas
@Tomas. thank you for replying so soon. Can u tell me any example where we have some three classes and we actually using has_one:through there and that cant be done using only two tables. as here i am not exact picture that what exactly the thing we are doing.Please just check the link of rails guide where they are giving a very suitable example of every associations. I thing there can be a better example to explain this has_one:through association
piemesons
This example is good. As nathanvda has writen in his answer you might not be able to change the database structure. But theoretically it is always possible to store the data from two tables in one table (or even from 3 tables into one as in your example) if you use a one-to-one relationship (`has_one`). There are no rules for this. You just structure your data so it's logically distributed and not just everything in one table.
Tomas Markauskas
@Tomas so it means has one through is just restructuring the data values? am i right cause i am still not able to understand WHY has_one:through?sry..looks like i am dumb. But better i say this honestly that still i am not clear.
piemesons
If you don't add `has_one :account_history, :through => :account` then you will have to write `@supplier.account.account_history` to access the history for a supplier so it is really just for your convenience as it would still work without that line.
Tomas Markauskas
@Tomas check this http://ryandaigle.com/articles/2008/3/24/what-s-new-in-edge-rails-has-one-through
piemesons
This is like I said just a convenience for you. Without `:through` you would have to do `@ryan.subscriptions << Subscription.create(:magazine => Magazine.create(:name => 'Hustler'))` which looks not as nice as ` @ryan.magazine = Magazine.create(:name => 'Hustler')`
Tomas Markauskas
@thomas thnku:-)
piemesons
+1  A: 

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
nathanvda