Tatva-Artha

meaning of "it"

Ruby ActiveRecord: How to implement has_few using has_one and belongs_to

without comments

Google search for has_one vs. belongs_to reveals that the concept is a little confusing and it takes some practice for it to settle in. From Ruby Doc, it appears that in a one-to-one cardinality, we are free to choose which class holds “has_one” and which holds “belongs_to”, with belongs_to class’s database table schema holding the foreign key relationship.

Depending on your business case, one object comprises of other and hence the comprising object ends up having “has_one” side of it. For example, a mortgage application has a borrower_profile. And hence, one can say that borrower_profile belongs to a mortgage application. This is modelled as:

class MortgageApplication
  has_one :borrower_profile
end
class BorrowerProfile
  belongs_to :mortgage_application
end

So far, it’s good.

However, what happens when a mortgage application allows a co-borrower profile? Now we have one-to-TWO relationship that we need to model. One way to model this would be to think of it as one-to-many relationship and add custom validation to ensure that there are a maximum of 2 borrowers for a given mortgage application.

class MortgageApplication
  has_many :borrower_profiles, :order => "borrower_order ASC"
end
class BorrowerProfile
  belongs_to :mortgage_application
end

The :o rder option is needed to ensure that first item in list is the borrower and second item is the co-borrower.

That could work. But, strictly speaking a mortgage application has-one borrower and has-one co-borrower. So, can we model it using has-one/belongs-to? Let’s try.

class MortgageApplication
  has_one :borrower_profile # default: table = borrower_profiles, foreign_key = mortgage_application_id
  has_one :coborrower_profile, :class_name => "BorrowerProfile", :foreign_key => :mortgage_application_id
end
class BorrowerProfile
  belongs_to :mortgage_application
end

The above code won’t work since both borrower_profile and coborrower_profile are being looked up using same foreign key. The problem here is that MortgageApplication needs to foreign key to refer to each of the borrowers. The problem can be resolved by keeping the foreign keys in the mortgage_applications table instead of borrower_profiles table, effectively flipping the has-one/belongs-to relationship. Here we go:

class MortgageApplication
  belongs_to :borrower_profile # default: table = "BorrowerProfile", foreign_key = "borrower_profile_id"
  belongs_to :coborrower_profile, :class_name => "BorrowerProfile", :foreign_key => "coborrower_profile_id"
end
class BorrowerProfile
  has_one :mortgage_application
end
 
# Problem: coborrower.mortgage_application will always be null

The above will solve the problem with mortgage_application pointers to borrower_profile. However, bi-directional relationship from borrower to mortgage-application will only work for primary borrower and not for co-borrower. This is because the lookup for mortgage-application from borrower model will always be attempted using foreign-key: borrower_profile_id.

So, we do the following:

class MortgageApplication
  belongs_to :borrower_profile # default: table = "BorrowerProfile", foreign_key = "borrower_profile_id"
  belongs_to :coborrower_profile, :class_name => "BorrowerProfile", :foreign_key => "coborrower_profile_id"
end
class BorrowerProfile
  has_one :mortgage_application
  # coborrower will use following reference to mortgage-application
  has_one :comortgage_application, :class_name => "MortgageApplication", :foreign_key => "coborrower_profile_id"
end

The above code solves that problem of reverse reference from borrower-profile to mortgage-application for both borrower as well as co-borrower. The only wrinkle is that when looking up mortgage application for co-borrower, we have to use comortgage_application reference. This wrinkle could be wrapped inside the object by overriding “mortgage_application” method.

As we can see, it is possible to map has-few relationship using either has_many/belongs_to or has_one/belongs_to. Each approach has some wrinkles and we can use either depending on which fits our style of programming.

http://www.tatvartha.com/wp-content/plugins/sociofluid/images/digg_16.png http://www.tatvartha.com/wp-content/plugins/sociofluid/images/reddit_16.png http://www.tatvartha.com/wp-content/plugins/sociofluid/images/stumbleupon_16.png http://www.tatvartha.com/wp-content/plugins/sociofluid/images/delicious_16.png http://www.tatvartha.com/wp-content/plugins/sociofluid/images/google_16.png http://www.tatvartha.com/wp-content/plugins/sociofluid/images/twitter_16.png

No related posts.

Related posts brought to you by Yet Another Related Posts Plugin.

Written by Sharad

November 8th, 2009 at 11:16 pm

Posted in All

Leave a Reply