ActiveRecord associations and more Part 2

Ali Erbay
3 min readSep 22, 2020

--

In the previous part of this series, I went over main Active Record one-to-many associations such as belongs_to, has_one, has_one :through and many-to-many associations such as has_many, has_many :through, has_and_belongs_to_many. We have seen that Active Record makes a developer’s life much easier with its simple and intuitive to use syntax. You can read it through this link.

Polymorphic Associations

Ruby on Rails Guide states “with polymorphic associations, a model can belong to more than one other model, on a single association”. The definition indicates that this type of polymorphic association has the potential to make our code DRY.

Let’s consider a case where we have Restaurant and Event models and we want to have Reviews model that can belong to a Restaurant or an Event. Review model must act reusable and let the other two models connect to it by an interface. The convention says that this interface should have “-able” suffix after its name as can be seen below;

Now we have the ability to know what a specific instance of review belongs to ( Event or Restaurant) by using@review.reviewable and we can find all reviews attached to Restaurant or Event by @restaurant.reviews or @event.reviews .

We need to have a line of code in the migration file to make it work;

You can create this migration file with the following command;

rails g migration create_reviews body:string reviewable:references{polymorphic}

has_many :through or has_and_belongs_to_many ?

Previously we saw that has_and_belongs_to_many association removes the need of a join model between two connecting models and only has a join table.

has_and_belongs_to_many association
has_many :through association

It looks like less work to use a has_and_belongs_to_many association but it can affect your work costing a lot of time in the future. When should we use one of them rather than the other?

  • The first thing that comes to my mind is that the case when Manifest model has to have some data for example name or description field. In this case, we can never validate this data in has_and_belongs_to_many association since there is no model of Manifest. The following model can be used in has_many :through association
Manifest model can have validations
  • The advantage of having a separate model is that you can add additional methods to your existing model and use it as a shared functionality in your codebase.
  • Another difference we can mention is the creation of model instances. With has_many :through we can create a Part model instance with the following way

part.manifest.create(assembly: Assembly.create(name:'assembly 1'))

Notice that we have to use manifest to connect part and assembly.

Let’s try to create the same with has_and_belongs_to_many association,

part.assemblies.create(name:'part 1')

  • has_and_belongs_to_many association is much more suitable when you are working with an already built database. Creating an additional model when you try to implement has_many :through association might break some parts of the system.

Conclusion

Official Ruby on Rails guide really gives good advice on this issue,

The simplest rule of thumb is that you should set up a has_many :through relationship if you need to work with the relationship model as an independent entity. If you don't need to do anything with the relationship model, it may be simpler to set up a has_and_belongs_to_many relationship (though you'll need to remember to create the joining table in the database).

You should use has_many :through if you need validations, callbacks, or extra attributes on the join model.

In my opinion, it is better to stick with has_many :through association because they tend to scale well as your app requires more functionality. Yes, it requires more work than implementinghas_and_belongs_to_many but I don’t believe it justifies losing the versatility of your database.

--

--

Ali Erbay

Full Stack Developer BDD/TDD || Ruby on Rails || React || React Native || NodeJS || Express https://github.com/kermit-klein