May 11, 2015 by Daniel P. Clark

HABTM; You Don’t Need It

My point: “For any valid use of implementing a has_and_belongs_to_many there is an equally valid way to implement it without it.”

A few reasons: Rails had changed both the way it worked and removed it from reflections for a brief period (Rails version 4.1.0 and 4.1.1).  Also the preferred implementation is to use has_many :through.  See this former Rails Issue #14682 and the subsequent Pull Request #15210 on the details.  The reflection was added back in Rails 4.1.2, but this doesn’t seem to be desired for access as a public api (source).

But I’d like to go even further and say you can keep it simpler, get the job done, and be able to manage your data far more easily with just using the basics.

Let’s look at an example from from Kick-Off: First Rails 4 Application with HABTM Association.  He uses an example where a person HABTM communities and a community HABTM persons.

Now whereas this is true in a sense that they both have and belongs to many of the other, it leaves the relationship of how this is true as unknown.  For example, as person can belong to a community as a member, owner, founder, etc.  This is a perfect place to use a has_many :through and then define your members/founders as such.  Then when you select the community.founders it will give a results that makes sense by the context it is given.

Now you can even go a step further away from HABTM, if you wanted to, and just create a membership table.  Create a polymorphic membership table so you can define memberships for any other Rails object you’d like.

The advantages of polymorphic relations are countless.  If you are unfamiliar with polymorphic relations I’ve given a thorough post on it here: Rails Polymorphic Associations .  Polymorphic records can be assigned to any ActiveRecord model; even itself.  That’s right I’ve written an example model here: Coffee Model and a test to verify the relationship is valid here: Minitest Spec Example.  And everything passes all GREEN ^_^.  So polymorphic models are your best friend.

Since a person is always the one implied when a membership is involved here we’ll create a field person_id.  But other than that the polymorphic membership model will determine what the membership belongs to.

Then update the models

Then you can create memberships on your community rather simply.  First we’ll create a person and a community, then the membership with both.

Look ma!  No HABTMs!  In the last line I show looking up memberships for the community.  You can do the same thing for a person object to find the memberships a person belongs to e.g.) Person.first.memberships

Code is easier to maintain if you stick to what is simple and basic.  I’ve come to appreciate this over time as I’ve written a library that’s tied heavily into all this relationship-workings (PolyBelongsTo).  I hope I can save many people from headaches by doing things the hard way… even unknowingly.

Summary

I believe I’ve made my point on HABTM.  If you can see a counter-argument to what I’ve presented here I’d love to hear about it.  Often times we over-think things and overcomplicate them.  When the best thing to do is often to take a step back, simplify it, and keep it simple.

Please feel free to comment, share, subscribe to my RSS Feed, and follow me on twitter @6ftdan!

God Bless!
-Daniel P. Clark

Image by Len Matthews via the Creative Commons Attribution-NonCommercial-ShareAlike 2.0 Generic License.

#belongs to#easy#habtm#has many#has_many_and_belongs_to#rails#simplify