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

4
Leave a Reply

avatar
2 Comment threads
2 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
3 Comment authors
Nathan LaddDaniel P. ClarkDev Recent comment authors
  Subscribe  
newest oldest most voted
Notify of
Dev
Guest
Dev

Fixing HABTM by using Polymorphic Relations? Thanks, no thanks.

Daniel P. Clark
Guest

The HABTM example gives no clear definition on the relation. Polymorphic relations are as flexible as you could need. If nothing else it’s recommended to use has_many :through rather than has_and_belongs_to_many. Or you can make a nonpolymorphic table memberships with person_id and community_id … it’s just a regular database table. The biggest issue with HABTM is that you have to write a whole extra set of ways of handling relationships. It’s handled differently and requires additional code for its use case. While it would be much easier to use one uniform approach which permits a meta-programming friendly way of doing… Read more »

Nathan Ladd
Guest
Nathan Ladd

HABTM has nice syntax with fixtures:

# persons.yml
joe:
communities: ruby, ios

# communities.yml
ruby:
etc.

HABTM is the right fit for pure many to many join tables. It results in the least amount of complexity and the cheapest maintenance long term.

When you need more, it’s easy to switch to HM:T.

Daniel P. Clark
Guest

Thanks for your input! I did not know. I’d be very interested in learning more behind the statements you made of “least complexity” and “cheapest maintenance”. Are these based on not having a middle relational term? (such as membership) If so is that what makes it simpler/cheaper since there is “technically less” to deal with?