Ruby ActiveRecord: How to implement has_few using has_one and belongs_to
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
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.
No related posts.
Related posts brought to you by Yet Another Related Posts Plugin.





